<?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=By1517</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=By1517"/>
	<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/wiki/Special:Contributions/By1517"/>
	<updated>2026-05-17T08:16:33Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796671</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796671"/>
		<updated>2019-11-20T11:52:18Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 4: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Monte carlo simulation:&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #For loops loop through every single lattice sight and calculate the energy change due to its neighbors in 2 directions, this avoid double counting. &lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;        #Sums up all of the numbers in lattice sites&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;        #Flips spin of the chosen lattice site&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;        #Checks the current energy (After spin flip)&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;        #Calculates difference in energy&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt; &lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;        #If new configuration not accepted, the spin is flipped back and the initial energy before the spin flip is returned.&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;        &lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;        #Updates properties of the Ising lattice&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;-2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initial_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         energy_output = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE=new_energy - initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE&amp;gt;0:&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                  self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                  energy_output = initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation_output = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energy_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E2 + energy_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisation_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M2 + magnetisation_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energy_output, magnetisation_output)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Elist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Mlist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
     steps_to_ignore = 3000&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;        #Stores both energy and magnetisation as a class attribute, this eliminates the need for recalculation in monte carlo step. This allows calling directly from memory and reduce time wasted in          unnecessary calculation&lt;br /&gt;
         self.current_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #np.roll and np.multiply function is much faster than for loops due to not needing to perform individually calculations for every lattice site&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum( self.lattice )&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt; &lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = new_energy - self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
             random_number = np.random.random()        &amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is acceptoed.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt;= self.steps_to_ignore:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E = self.E + self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Elist.append(self.current_energy)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E2 = self.E2 + self.current_energy**2&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M = self.M + self.current_magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Mlist.append(self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M2 = self.M2 + self.current_magnetisation**2&amp;lt;br&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return (self.current_energy, self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0 and self.steps_to_ignore &amp;lt; self.n_cycles : #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #loads .dat files for each lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 data2x2=np.loadtxt(&amp;quot;2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data4x4=np.loadtxt(&amp;quot;4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data8x8=np.loadtxt(&amp;quot;8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data16x16=np.loadtxt(&amp;quot;16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data32x32=np.loadtxt(&amp;quot;32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Creates list of temperature values&amp;lt;br&amp;gt;&lt;br /&gt;
 temps=(data2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #defining a function to return heat capacity values&amp;lt;br&amp;gt;&lt;br /&gt;
 def C(E,E2,T):&amp;lt;br&amp;gt;&lt;br /&gt;
     return (E2-E**2)/(T**2)&amp;lt;br&amp;gt;&lt;br /&gt;
 C2x2=C(data2x2[:,1],data2x2[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C4x4=C(data4x4[:,1],data4x4[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C8x8=C(data8x8[:,1],data8x8[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C16x16=C(data16x16[:,1],data16x16[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C32x32=C(data32x32[:,1],data32x32[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots heat capacity against temperature for all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C2x2/4, label=&amp;quot;2x2&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C4x4/16, label=&amp;quot;4x4&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C8x8/64, label=&amp;quot;8x8&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C16x16/256, label=&amp;quot;16x16&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C32x32/1024,label=&amp;quot;32x32&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.png&#039;,dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.svg&#039;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.show()&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used.[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Choosing 32x32 lattice to fit the data, the script would be:&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 fit3rd32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit5th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit7th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit9th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values3rd = np.polyval(fit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values5th = np.polyval(fit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values7th = np.polyval(fit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values9th = np.polyval(fit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#save plots&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Modifying previous script to only fit near peak&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Tempreatures chosen by visual inspection&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 selection = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax) #choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit3rd32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit5th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit7th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values3rd = np.polyval(peakfit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values5th = np.polyval(peakfit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values7th = np.polyval(peakfit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#Save plot&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Performing polyfits of 9th order to the rest of the lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata2x2=np.loadtxt(&amp;quot;C++_data/2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata4x4=np.loadtxt(&amp;quot;C++_data/4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata8x8=np.loadtxt(&amp;quot;C++_data/8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata16x16=np.loadtxt(&amp;quot;C++_data/16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Setting temperature range to polyfit&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assinging temp range from data to variable&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC2x2=(cdata2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC4x4=(cdata4x4[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC8x8=(cdata8x8[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC16x16=(cdata16x16[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assign a chosen t range to a variable&amp;lt;br&amp;gt;&lt;br /&gt;
 selection2x2 = np.logical_and(tempsC2x2 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection4x4 = np.logical_and(tempsC4x4 &amp;gt; Tmin, tempsC4x4 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection8x8 = np.logical_and(tempsC8x8 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection16x16 = np.logical_and(tempsC16x16 &amp;gt; Tmin, tempsC16x16 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection32x32 = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax)#choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 #Fitting 9th order polynomial to all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection32x32], cdata32x32[:,5][selection32x32], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th16x16=np.polyfit(tempsC16x16[selection16x16], cdata16x16[:,5][selection16x16], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th8x8=np.polyfit(tempsC8x8[selection8x8], cdata8x8[:,5][selection8x8], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th4x4=np.polyfit(tempsC4x4[selection4x4], cdata4x4[:,5][selection4x4], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th2x2=np.polyfit(tempsC2x2[selection2x2], cdata2x2[:,5][selection2x2], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th32x32 = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th16x16 = np.polyval(peakfit9th16x16, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th8x8 = np.polyval(peakfit9th8x8, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th4x4 = np.polyval(peakfit9th4x4, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th2x2 = np.polyval(peakfit9th2x2, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Extracting Curie Temperature&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax2x2=np.max(peakfitted_C_values9th2x2)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax4x4=np.max(peakfitted_C_values9th4x4)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax8x8=np.max(peakfitted_C_values9th8x8)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax16x16=np.max(peakfitted_C_values9th16x16)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax32x32=np.max(peakfitted_C_values9th32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax2x2=T_range[peakfitted_C_values9th2x2==Cmax2x2]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax4x4=T_range[peakfitted_C_values9th4x4==Cmax4x4]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax8x8=T_range[peakfitted_C_values9th8x8==Cmax8x8]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax16x16=T_range[peakfitted_C_values9th16x16==Cmax16x16]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax32x32=T_range[peakfitted_C_values9th32x32==Cmax32x32]&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Saves a .dat file with side lengths and curie temp in 2 columns&amp;lt;br&amp;gt;&lt;br /&gt;
 np.savetxt(&amp;quot;CurieTemperature.dat&amp;quot;, np.column_stack(np.array([[2, 4, 8, 16, 32],[Tmax2x2,Tmax4x4,Tmax8x8,Tmax16x16,Tmax32x32]])))&lt;br /&gt;
The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;br /&gt;
&lt;br /&gt;
The following zip file is a jupyter notebook containing scripts used in section 5 - 8.&lt;br /&gt;
[[Media:BY1517 2D Ising model.zip|BY1517 2D Ising model.zip]]&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796670</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796670"/>
		<updated>2019-11-20T11:51:15Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 4: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Monte carlo simulation:&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #For loops loop through every single lattice sight and calculate the energy change due to its neighbors in 2 directions, this avoid double counting. &lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;        #Sums up all of the numbers in lattice sites&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;        #Flips spin of the chosen lattice site&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;        #Checks the current energy (After spin flip)&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;        #Calculates difference in energy&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt; &lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;        #If new configuration not accepted, the spin is flipped back and the initial energy before the spin flip is returned.&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;        &lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;        #Updates properties of the Ising lattice&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;-2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initial_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         energy_output = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE=new_energy - initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE&amp;gt;0:&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                  self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                  energy_output = initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation_output = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energy_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E2 + energy_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisation_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M2 + magnetisation_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energy_output, magnetisation_output)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Elist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Mlist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
     steps_to_ignore = 3000&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;        #Stores both energy and magnetisation as a class attribute, this eliminates the need for recalculation in monte carlo step. This allows calling directly from memory and reduce time wasted in          unnecessary calculation&lt;br /&gt;
         self.current_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #np.roll and np.multiply function is much faster than for loops due to not needing to perform individually calculations for every lattice site&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum( self.lattice )&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt; &lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = new_energy - self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
             random_number = np.random.random()        &amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is acceptoed.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt;= self.steps_to_ignore:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E = self.E + self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Elist.append(self.current_energy)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E2 = self.E2 + self.current_energy**2&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M = self.M + self.current_magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Mlist.append(self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M2 = self.M2 + self.current_magnetisation**2&amp;lt;br&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return (self.current_energy, self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0 and self.steps_to_ignore &amp;lt; self.n_cycles : #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #loads .dat files for each lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 data2x2=np.loadtxt(&amp;quot;2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data4x4=np.loadtxt(&amp;quot;4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data8x8=np.loadtxt(&amp;quot;8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data16x16=np.loadtxt(&amp;quot;16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data32x32=np.loadtxt(&amp;quot;32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Creates list of temperature values&amp;lt;br&amp;gt;&lt;br /&gt;
 temps=(data2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #defining a function to return heat capacity values&amp;lt;br&amp;gt;&lt;br /&gt;
 def C(E,E2,T):&amp;lt;br&amp;gt;&lt;br /&gt;
     return (E2-E**2)/(T**2)&amp;lt;br&amp;gt;&lt;br /&gt;
 C2x2=C(data2x2[:,1],data2x2[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C4x4=C(data4x4[:,1],data4x4[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C8x8=C(data8x8[:,1],data8x8[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C16x16=C(data16x16[:,1],data16x16[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C32x32=C(data32x32[:,1],data32x32[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots heat capacity against temperature for all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C2x2/4, label=&amp;quot;2x2&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C4x4/16, label=&amp;quot;4x4&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C8x8/64, label=&amp;quot;8x8&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C16x16/256, label=&amp;quot;16x16&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C32x32/1024,label=&amp;quot;32x32&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.png&#039;,dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.svg&#039;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.show()&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used.[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Choosing 32x32 lattice to fit the data, the script would be:&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 fit3rd32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit5th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit7th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit9th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values3rd = np.polyval(fit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values5th = np.polyval(fit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values7th = np.polyval(fit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values9th = np.polyval(fit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#save plots&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Modifying previous script to only fit near peak&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Tempreatures chosen by visual inspection&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 selection = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax) #choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit3rd32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit5th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit7th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values3rd = np.polyval(peakfit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values5th = np.polyval(peakfit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values7th = np.polyval(peakfit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#Save plot&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Performing polyfits of 9th order to the rest of the lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata2x2=np.loadtxt(&amp;quot;C++_data/2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata4x4=np.loadtxt(&amp;quot;C++_data/4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata8x8=np.loadtxt(&amp;quot;C++_data/8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata16x16=np.loadtxt(&amp;quot;C++_data/16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Setting temperature range to polyfit&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assinging temp range from data to variable&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC2x2=(cdata2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC4x4=(cdata4x4[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC8x8=(cdata8x8[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC16x16=(cdata16x16[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assign a chosen t range to a variable&amp;lt;br&amp;gt;&lt;br /&gt;
 selection2x2 = np.logical_and(tempsC2x2 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection4x4 = np.logical_and(tempsC4x4 &amp;gt; Tmin, tempsC4x4 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection8x8 = np.logical_and(tempsC8x8 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection16x16 = np.logical_and(tempsC16x16 &amp;gt; Tmin, tempsC16x16 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection32x32 = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax)#choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 #Fitting 9th order polynomial to all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection32x32], cdata32x32[:,5][selection32x32], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th16x16=np.polyfit(tempsC16x16[selection16x16], cdata16x16[:,5][selection16x16], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th8x8=np.polyfit(tempsC8x8[selection8x8], cdata8x8[:,5][selection8x8], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th4x4=np.polyfit(tempsC4x4[selection4x4], cdata4x4[:,5][selection4x4], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th2x2=np.polyfit(tempsC2x2[selection2x2], cdata2x2[:,5][selection2x2], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th32x32 = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th16x16 = np.polyval(peakfit9th16x16, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th8x8 = np.polyval(peakfit9th8x8, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th4x4 = np.polyval(peakfit9th4x4, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th2x2 = np.polyval(peakfit9th2x2, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Extracting Curie Temperature&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax2x2=np.max(peakfitted_C_values9th2x2)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax4x4=np.max(peakfitted_C_values9th4x4)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax8x8=np.max(peakfitted_C_values9th8x8)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax16x16=np.max(peakfitted_C_values9th16x16)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax32x32=np.max(peakfitted_C_values9th32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax2x2=T_range[peakfitted_C_values9th2x2==Cmax2x2]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax4x4=T_range[peakfitted_C_values9th4x4==Cmax4x4]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax8x8=T_range[peakfitted_C_values9th8x8==Cmax8x8]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax16x16=T_range[peakfitted_C_values9th16x16==Cmax16x16]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax32x32=T_range[peakfitted_C_values9th32x32==Cmax32x32]&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Saves a .dat file with side lengths and curie temp in 2 columns&amp;lt;br&amp;gt;&lt;br /&gt;
 np.savetxt(&amp;quot;CurieTemperature.dat&amp;quot;, np.column_stack(np.array([[2, 4, 8, 16, 32],[Tmax2x2,Tmax4x4,Tmax8x8,Tmax16x16,Tmax32x32]])))&lt;br /&gt;
The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;br /&gt;
&lt;br /&gt;
The following file is a jupyter notebook containing scripts used in section 5 - 8.&lt;br /&gt;
&lt;br /&gt;
BY1517 2D Ising model.zip&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:BY1517_2D_Ising_model.zip&amp;diff=796664</id>
		<title>File:BY1517 2D Ising model.zip</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:BY1517_2D_Ising_model.zip&amp;diff=796664"/>
		<updated>2019-11-20T11:48:51Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796656</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796656"/>
		<updated>2019-11-20T11:40:44Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 2. Calculating the energy and magnetisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Monte carlo simulation:&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #For loops loop through every single lattice sight and calculate the energy change due to its neighbors in 2 directions, this avoid double counting. &lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;        #Sums up all of the numbers in lattice sites&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;        #Flips spin of the chosen lattice site&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;        #Checks the current energy (After spin flip)&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;        #Calculates difference in energy&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt; &lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;        #If new configuration not accepted, the spin is flipped back and the initial energy before the spin flip is returned.&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;        &lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;        #Updates properties of the Ising lattice&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;-2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initial_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         energy_output = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE=new_energy - initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE&amp;gt;0:&amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is accepted.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                  self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                  energy_output = initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation_output = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energy_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E2 + energy_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisation_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M2 + magnetisation_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energy_output, magnetisation_output)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Elist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Mlist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
     steps_to_ignore = 3000&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;        #Stores both energy and magnetisation as a class attribute, this eliminates the need for recalculation in monte carlo step. This allows calling directly from memory and reduce time wasted in          unnecessary calculation&lt;br /&gt;
         self.current_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;        #np.roll and np.multiply function is much faster than for loops due to not needing to perform individually calculations for every lattice site&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum( self.lattice )&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt; &lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = new_energy - self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
             random_number = np.random.random()        &amp;lt;br&amp;gt;        #Check if energy change is beneficial&lt;br /&gt;
         #If energy change not beneficial, it undergoes a probability check where if it passes, the new configuraiton is acceptoed.&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt;= self.steps_to_ignore:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E = self.E + self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Elist.append(self.current_energy)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E2 = self.E2 + self.current_energy**2&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M = self.M + self.current_magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Mlist.append(self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M2 = self.M2 + self.current_magnetisation**2&amp;lt;br&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return (self.current_energy, self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0 and self.steps_to_ignore &amp;lt; self.n_cycles : #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #loads .dat files for each lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 data2x2=np.loadtxt(&amp;quot;2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data4x4=np.loadtxt(&amp;quot;4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data8x8=np.loadtxt(&amp;quot;8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data16x16=np.loadtxt(&amp;quot;16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data32x32=np.loadtxt(&amp;quot;32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Creates list of temperature values&amp;lt;br&amp;gt;&lt;br /&gt;
 temps=(data2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #defining a function to return heat capacity values&amp;lt;br&amp;gt;&lt;br /&gt;
 def C(E,E2,T):&amp;lt;br&amp;gt;&lt;br /&gt;
     return (E2-E**2)/(T**2)&amp;lt;br&amp;gt;&lt;br /&gt;
 C2x2=C(data2x2[:,1],data2x2[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C4x4=C(data4x4[:,1],data4x4[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C8x8=C(data8x8[:,1],data8x8[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C16x16=C(data16x16[:,1],data16x16[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C32x32=C(data32x32[:,1],data32x32[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots heat capacity against temperature for all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C2x2/4, label=&amp;quot;2x2&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C4x4/16, label=&amp;quot;4x4&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C8x8/64, label=&amp;quot;8x8&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C16x16/256, label=&amp;quot;16x16&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C32x32/1024,label=&amp;quot;32x32&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.png&#039;,dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.svg&#039;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.show()&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used.[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Choosing 32x32 lattice to fit the data, the script would be:&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 fit3rd32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit5th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit7th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit9th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values3rd = np.polyval(fit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values5th = np.polyval(fit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values7th = np.polyval(fit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values9th = np.polyval(fit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#save plots&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Modifying previous script to only fit near peak&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Tempreatures chosen by visual inspection&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 selection = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax) #choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit3rd32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit5th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit7th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values3rd = np.polyval(peakfit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values5th = np.polyval(peakfit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values7th = np.polyval(peakfit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;#Save plot&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Performing polyfits of 9th order to the rest of the lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata2x2=np.loadtxt(&amp;quot;C++_data/2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata4x4=np.loadtxt(&amp;quot;C++_data/4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata8x8=np.loadtxt(&amp;quot;C++_data/8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata16x16=np.loadtxt(&amp;quot;C++_data/16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Setting temperature range to polyfit&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assinging temp range from data to variable&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC2x2=(cdata2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC4x4=(cdata4x4[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC8x8=(cdata8x8[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC16x16=(cdata16x16[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assign a chosen t range to a variable&amp;lt;br&amp;gt;&lt;br /&gt;
 selection2x2 = np.logical_and(tempsC2x2 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection4x4 = np.logical_and(tempsC4x4 &amp;gt; Tmin, tempsC4x4 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection8x8 = np.logical_and(tempsC8x8 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection16x16 = np.logical_and(tempsC16x16 &amp;gt; Tmin, tempsC16x16 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection32x32 = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax)#choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 #Fitting 9th order polynomial to all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection32x32], cdata32x32[:,5][selection32x32], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th16x16=np.polyfit(tempsC16x16[selection16x16], cdata16x16[:,5][selection16x16], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th8x8=np.polyfit(tempsC8x8[selection8x8], cdata8x8[:,5][selection8x8], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th4x4=np.polyfit(tempsC4x4[selection4x4], cdata4x4[:,5][selection4x4], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th2x2=np.polyfit(tempsC2x2[selection2x2], cdata2x2[:,5][selection2x2], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th32x32 = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th16x16 = np.polyval(peakfit9th16x16, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th8x8 = np.polyval(peakfit9th8x8, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th4x4 = np.polyval(peakfit9th4x4, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th2x2 = np.polyval(peakfit9th2x2, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Extracting Curie Temperature&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax2x2=np.max(peakfitted_C_values9th2x2)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax4x4=np.max(peakfitted_C_values9th4x4)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax8x8=np.max(peakfitted_C_values9th8x8)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax16x16=np.max(peakfitted_C_values9th16x16)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax32x32=np.max(peakfitted_C_values9th32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax2x2=T_range[peakfitted_C_values9th2x2==Cmax2x2]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax4x4=T_range[peakfitted_C_values9th4x4==Cmax4x4]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax8x8=T_range[peakfitted_C_values9th8x8==Cmax8x8]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax16x16=T_range[peakfitted_C_values9th16x16==Cmax16x16]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax32x32=T_range[peakfitted_C_values9th32x32==Cmax32x32]&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Saves a .dat file with side lengths and curie temp in 2 columns&amp;lt;br&amp;gt;&lt;br /&gt;
 np.savetxt(&amp;quot;CurieTemperature.dat&amp;quot;, np.column_stack(np.array([[2, 4, 8, 16, 32],[Tmax2x2,Tmax4x4,Tmax8x8,Tmax16x16,Tmax32x32]])))&lt;br /&gt;
The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796607</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796607"/>
		<updated>2019-11-20T10:45:01Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 2: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;-2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initial_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         energy_output = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE=new_energy - initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE&amp;gt;0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                  self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                  energy_output = initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation_output = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energy_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E2 + energy_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisation_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M2 + magnetisation_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energy_output, magnetisation_output)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Elist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Mlist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
     steps_to_ignore = 3000&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum( self.lattice )&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = new_energy - self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
             random_number = np.random.random()        &amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt;= self.steps_to_ignore:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E = self.E + self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Elist.append(self.current_energy)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E2 = self.E2 + self.current_energy**2&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M = self.M + self.current_magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Mlist.append(self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M2 = self.M2 + self.current_magnetisation**2&amp;lt;br&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return (self.current_energy, self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0 and self.steps_to_ignore &amp;lt; self.n_cycles : #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #loads .dat files for each lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 data2x2=np.loadtxt(&amp;quot;2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data4x4=np.loadtxt(&amp;quot;4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data8x8=np.loadtxt(&amp;quot;8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data16x16=np.loadtxt(&amp;quot;16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data32x32=np.loadtxt(&amp;quot;32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Creates list of temperature values&amp;lt;br&amp;gt;&lt;br /&gt;
 temps=(data2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #defining a function to return heat capacity values&amp;lt;br&amp;gt;&lt;br /&gt;
 def C(E,E2,T):&amp;lt;br&amp;gt;&lt;br /&gt;
     return (E2-E**2)/(T**2)&amp;lt;br&amp;gt;&lt;br /&gt;
 C2x2=C(data2x2[:,1],data2x2[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C4x4=C(data4x4[:,1],data4x4[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C8x8=C(data8x8[:,1],data8x8[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C16x16=C(data16x16[:,1],data16x16[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C32x32=C(data32x32[:,1],data32x32[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots heat capacity against temperature for all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C2x2/4, label=&amp;quot;2x2&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C4x4/16, label=&amp;quot;4x4&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C8x8/64, label=&amp;quot;8x8&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C16x16/256, label=&amp;quot;16x16&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C32x32/1024,label=&amp;quot;32x32&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.png&#039;,dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.svg&#039;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.show()&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used.[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Choosing 32x32 lattice to fit the data, the script would be:&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 fit3rd32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit5th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit7th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 fit9th32x32=np.polyfit(tempsC32x32, cdata32x32[:,5], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values3rd = np.polyval(fit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values5th = np.polyval(fit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values7th = np.polyval(fit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 fitted_C_values9th = np.polyval(fit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,fitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Modifying previous script to only fit near peak&amp;lt;br&amp;gt;&lt;br /&gt;
 #load c++ data files&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Tempreatures chosen by visual inspection&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 selection = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax) #choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Get first and sixth column of data for t and c and perform a polyfit over the range of data. Different degrees of polymial fitting attemped below are 3,5,7, and 9&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit3rd32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 3)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit5th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 5)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit7th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 7)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection], cdata32x32[:,5][selection], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values3rd = np.polyval(peakfit3rd32x32, T_range) # use the fit object to generate the corresponding values of C&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values5th = np.polyval(peakfit5th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values7th = np.polyval(peakfit7th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots all polynomialfitting&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(tempsC32x32,cdata32x32[:,5], label=&amp;quot;C++ simulated Data&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values3rd, label=&amp;quot;3rd order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values5th, label=&amp;quot;5th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values7th, label=&amp;quot;7th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(T_range,peakfitted_C_values9th, label=&amp;quot;9th order polyfit&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat Capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.png&amp;quot;, dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&amp;quot;32x32 lattice peak polyfits.svg&amp;quot;)&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. &lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #Performing polyfits of 9th order to the rest of the lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata2x2=np.loadtxt(&amp;quot;C++_data/2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata4x4=np.loadtxt(&amp;quot;C++_data/4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata8x8=np.loadtxt(&amp;quot;C++_data/8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata16x16=np.loadtxt(&amp;quot;C++_data/16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 cdata32x32=np.loadtxt(&amp;quot;C++_data/32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Setting temperature range to polyfit&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmin = 2.2&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax = 2.4&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assinging temp range from data to variable&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC2x2=(cdata2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC4x4=(cdata4x4[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC8x8=(cdata8x8[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC16x16=(cdata16x16[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 tempsC32x32=(cdata32x32[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #Assign a chosen t range to a variable&amp;lt;br&amp;gt;&lt;br /&gt;
 selection2x2 = np.logical_and(tempsC2x2 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection4x4 = np.logical_and(tempsC4x4 &amp;gt; Tmin, tempsC4x4 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection8x8 = np.logical_and(tempsC8x8 &amp;gt; Tmin, tempsC2x2 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection16x16 = np.logical_and(tempsC16x16 &amp;gt; Tmin, tempsC16x16 &amp;lt; Tmax)&amp;lt;br&amp;gt;&lt;br /&gt;
 selection32x32 = np.logical_and(tempsC32x32 &amp;gt; Tmin, tempsC32x32 &amp;lt; Tmax)#choose only those rows where both conditions are true&amp;lt;br&amp;gt;&lt;br /&gt;
 #Fitting 9th order polynomial to all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th32x32=np.polyfit(tempsC32x32[selection32x32], cdata32x32[:,5][selection32x32], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th16x16=np.polyfit(tempsC16x16[selection16x16], cdata16x16[:,5][selection16x16], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th8x8=np.polyfit(tempsC8x8[selection8x8], cdata8x8[:,5][selection8x8], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th4x4=np.polyfit(tempsC4x4[selection4x4], cdata4x4[:,5][selection4x4], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfit9th2x2=np.polyfit(tempsC2x2[selection2x2], cdata2x2[:,5][selection2x2], 9)&amp;lt;br&amp;gt;&lt;br /&gt;
 #now we generate interpolated values of the fitted polynomial over the range of our function&amp;lt;br&amp;gt;&lt;br /&gt;
 T_min = np.min(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_max = np.max(tempsC32x32[selection])&amp;lt;br&amp;gt;&lt;br /&gt;
 T_range = np.linspace(T_min, T_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th32x32 = np.polyval(peakfit9th32x32, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th16x16 = np.polyval(peakfit9th16x16, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th8x8 = np.polyval(peakfit9th8x8, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th4x4 = np.polyval(peakfit9th4x4, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 peakfitted_C_values9th2x2 = np.polyval(peakfit9th2x2, T_range)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Extracting Curie Temperature&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax2x2=np.max(peakfitted_C_values9th2x2)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax4x4=np.max(peakfitted_C_values9th4x4)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax8x8=np.max(peakfitted_C_values9th8x8)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax16x16=np.max(peakfitted_C_values9th16x16)&amp;lt;br&amp;gt;&lt;br /&gt;
 Cmax32x32=np.max(peakfitted_C_values9th32x32)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax2x2=T_range[peakfitted_C_values9th2x2==Cmax2x2]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax4x4=T_range[peakfitted_C_values9th4x4==Cmax4x4]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax8x8=T_range[peakfitted_C_values9th8x8==Cmax8x8]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax16x16=T_range[peakfitted_C_values9th16x16==Cmax16x16]&amp;lt;br&amp;gt;&lt;br /&gt;
 Tmax32x32=T_range[peakfitted_C_values9th32x32==Cmax32x32]&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 #Saves a .dat file with side lengths and curie temp in 2 columns&amp;lt;br&amp;gt;&lt;br /&gt;
 np.savetxt(&amp;quot;CurieTemperature.dat&amp;quot;, np.column_stack(np.array([[2, 4, 8, 16, 32],[Tmax2x2,Tmax4x4,Tmax8x8,Tmax16x16,Tmax32x32]])))&lt;br /&gt;
The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796602</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796602"/>
		<updated>2019-11-20T10:42:35Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Third year CMP compulsory experiment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;-2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|E&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|M&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initial_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         energy_output = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE=new_energy - initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE&amp;gt;0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                  self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                  energy_output = initial_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energy_output = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation_output = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energy_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E2 + energy_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisation_output&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M2 + magnetisation_output**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energy_output, magnetisation_output)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Elist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     Mlist = []&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
     steps_to_ignore = 3000&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 0))) - np.sum ( np.multiply ( self.lattice, np.roll( self.lattice, -1, axis = 1)))&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum( self.lattice )&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         new_energy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = new_energy - self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
             random_number = np.random.random()        &amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j] = - self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
             else:&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_energy = new_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.current_magnetisation = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt;= self.steps_to_ignore:&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E = self.E + self.current_energy&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Elist.append(self.current_energy)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.E2 = self.E2 + self.current_energy**2&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M = self.M + self.current_magnetisation&amp;lt;br&amp;gt;&lt;br /&gt;
             self.Mlist.append(self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
             self.M2 = self.M2 + self.current_magnetisation**2&amp;lt;br&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         return (self.current_energy, self.current_magnetisation)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0 and self.steps_to_ignore &amp;lt; self.n_cycles : #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/(self.n_cycles - self.steps_to_ignore)&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
 from matplotlib import pylab as pl&amp;lt;br&amp;gt;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 #loads .dat files for each lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 data2x2=np.loadtxt(&amp;quot;2x2.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data4x4=np.loadtxt(&amp;quot;4x4.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data8x8=np.loadtxt(&amp;quot;8x8.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data16x16=np.loadtxt(&amp;quot;16x16.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 data32x32=np.loadtxt(&amp;quot;32x32.dat&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Creates list of temperature values&amp;lt;br&amp;gt;&lt;br /&gt;
 temps=(data2x2[:,0])&amp;lt;br&amp;gt;&lt;br /&gt;
 #defining a function to return heat capacity values&amp;lt;br&amp;gt;&lt;br /&gt;
 def C(E,E2,T):&amp;lt;br&amp;gt;&lt;br /&gt;
     return (E2-E**2)/(T**2)&amp;lt;br&amp;gt;&lt;br /&gt;
 C2x2=C(data2x2[:,1],data2x2[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C4x4=C(data4x4[:,1],data4x4[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C8x8=C(data8x8[:,1],data8x8[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C16x16=C(data16x16[:,1],data16x16[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 C32x32=C(data32x32[:,1],data32x32[:,2],temps)&amp;lt;br&amp;gt;&lt;br /&gt;
 #Plots heat capacity against temperature for all lattice sizes&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C2x2/4, label=&amp;quot;2x2&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C4x4/16, label=&amp;quot;4x4&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C8x8/64, label=&amp;quot;8x8&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C16x16/256, label=&amp;quot;16x16&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.plot(temps,C32x32/1024,label=&amp;quot;32x32&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.ylabel(&amp;quot;Heat capacity&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.xlabel(&amp;quot;Temperature&amp;quot;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.legend()&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.png&#039;,dpi = 600)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.savefig(&#039;heatcap.svg&#039;)&amp;lt;br&amp;gt;&lt;br /&gt;
 pl.show()&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796590</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796590"/>
		<updated>2019-11-20T10:36:07Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 4: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;http://www.teori.atom.fysik.su.se/~lindroth/comp08/ising.pdf&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796584</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796584"/>
		<updated>2019-11-20T10:33:19Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 4: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
&#039;&#039;&#039;Find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=\frac{2J}{{{k}_{b}}\ln (1+\sqrt{2})}&amp;lt;/math&amp;gt;. Taking J as 1, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C}}^{Onsager}=2.269&amp;lt;/math&amp;gt;. This represents a difference of 0.088%, which is minimal and is surprisingly in very good agreement with the simulation. The most significant error would have been fluctuations that happened in the simulation.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796565</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796565"/>
		<updated>2019-11-20T10:22:15Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 4: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
F&#039;&#039;&#039;ind the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272. The literature equation for the Curie temperature of an infinite 2D square lattice is given as&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796557</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796557"/>
		<updated>2019-11-20T10:15:45Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 8. Locating the Curie Temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039; &lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 3:&#039;&#039;&#039; &#039;&#039;&#039; ===&lt;br /&gt;
&#039;&#039;&#039;Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
=== Task 4: ===&lt;br /&gt;
F&#039;&#039;&#039;ind the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
[[File:CTempinf.png|centre|thumb|Figure 14 - Plot of inverse of sidelength against Curie Temperature with linear fit]]&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit and gives a value of 2.272.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:CTempinf.png&amp;diff=796542</id>
		<title>File:CTempinf.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:CTempinf.png&amp;diff=796542"/>
		<updated>2019-11-20T10:10:54Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796537</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796537"/>
		<updated>2019-11-20T10:09:26Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 8. Locating the Curie Temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,L}}=\frac{A}{L}+{{T}_{C,\infty }}&amp;lt;/math&amp;gt;. Using this relationship, the Curie temperature of each lattice size was plotted against the inverse of the sidelength. Then a linear function was fitted against the data. By finding the Y-intercept, &lt;br /&gt;
&amp;lt;math&amp;gt;{{T}_{C,\infty }}&amp;lt;/math&amp;gt;&lt;br /&gt;
can be found. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Y-intercept is given as the last constant term of the polyfit&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796518</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796518"/>
		<updated>2019-11-20T10:00:41Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 8. Locating the Curie Temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code [https://github.com/niallj/ducking-avenger/tree/master/Ising here] if you are interested. Each file contains six columns: &amp;lt;math&amp;gt;T, E, E^2, M, M^2, C&amp;lt;/math&amp;gt; (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For &#039;&#039;one&#039;&#039; lattice size, save a PNG of this comparison and add it to your report &amp;amp;mdash; add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation [http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.legend here]).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; for that side length. Make a plot that uses the scaling relation given above to determine &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796513</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796513"/>
		<updated>2019-11-20T09:59:20Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 8. Locating the Curie Temperature */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Temperature vs heat capacity for all lattice sizes using python simulation]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations. For the following analysis, the lattice size of 32x32 was used. &lt;br /&gt;
[[File:By1517Python vs C++ 32x32.png|centre|thumb|Figure 11 - Temperature against heat capacity using C++ simulation data and python simulation data for a 32x32 lattice]]&lt;br /&gt;
To obtain heat capacity and temperature of the peak, polynomials were fitted against the C++ simulated data using the np.polyfit function. A number of different polynomial degrees were used. &lt;br /&gt;
[[File:32x32 lattice polyfits.png|centre|thumb|Figure 12 - Plot of temperature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted onto the data for a 32x32 lattice]]&lt;br /&gt;
As the graph above shows, the higher the order, the better the polynomial is at fitting the data. However none of those still provide a satisfactory fit due to the fitting function attempting to fit across all data points.&lt;br /&gt;
To solve this the range at which the polyfit function will try to fit is narrowed down as amuch as possible towards the peak present in the data.&lt;br /&gt;
[[File:32x32 lattice peak polyfits.png|centre|thumb|Figure 13 - Plot of tempeature against heat capacity for C++ simulation data and 4 polynomials with different orders fitted to a narrow range of data centered around the peak for a 32x32 lattice]]&lt;br /&gt;
The curie temperatures for lattice sizes 2x2, 4x4, 8x8 and 16x16 were found similarly and the data was saved as a CurieTemperature.dat file. The analysis yielded the following:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Side length&lt;br /&gt;
!Curie Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|2.39&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|2.37&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|2.21&lt;br /&gt;
|-&lt;br /&gt;
|16&lt;br /&gt;
|2.33&lt;br /&gt;
|-&lt;br /&gt;
|32&lt;br /&gt;
|3.30&lt;br /&gt;
|}&lt;br /&gt;
The relationship between the Curie temperature and the side length of the square lattice is given as&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:32x32_lattice_peak_polyfits.png&amp;diff=796483</id>
		<title>File:32x32 lattice peak polyfits.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:32x32_lattice_peak_polyfits.png&amp;diff=796483"/>
		<updated>2019-11-20T09:32:04Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:32x32_lattice_polyfits.png&amp;diff=796482</id>
		<title>File:32x32 lattice polyfits.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:32x32_lattice_polyfits.png&amp;diff=796482"/>
		<updated>2019-11-20T09:32:03Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796471</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796471"/>
		<updated>2019-11-20T09:13:37Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517Heatcap.png|centre|thumb|Figure 11 - Inverse side length vs heat capacity for all lattice sizes]]&lt;br /&gt;
&lt;br /&gt;
== 8. Locating the Curie Temperature ==&lt;br /&gt;
Data obtained from C++ simulations are provided and are compared with the data obtained from running python simulations.&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Python_vs_C%2B%2B_32x32.png&amp;diff=796470</id>
		<title>File:By1517Python vs C++ 32x32.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Python_vs_C%2B%2B_32x32.png&amp;diff=796470"/>
		<updated>2019-11-20T09:13:06Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Heatcap.png&amp;diff=796468</id>
		<title>File:By1517Heatcap.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Heatcap.png&amp;diff=796468"/>
		<updated>2019-11-20T09:09:42Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796465</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796465"/>
		<updated>2019-11-20T09:04:36Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 6. Effect of system size */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Property&lt;br /&gt;
!Value&lt;br /&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;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
 &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]In general, as the lattice size increase, the standard deviation for average energy and magnetisation per spin decreases for all temperatures. The trend appears to stabilize when the lattice size is 16x16 and 32x32 in addition to being very similar to each other, which is more easily seen when they are plotted on the same graph.&lt;br /&gt;
[[File:By1517Allsize.png|centre|thumb|Figure 9 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for all size lattices]]&lt;br /&gt;
&lt;br /&gt;
This would suggest that lattice size of 16x16 or larger should be used in order to capture long range fluctuations.&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Allsize.png&amp;diff=796460</id>
		<title>File:By1517Allsize.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517Allsize.png&amp;diff=796460"/>
		<updated>2019-11-20T08:53:36Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796450</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796450"/>
		<updated>2019-11-20T08:32:47Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 2: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;(Results combined with section 6)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 6. Effect of system size ==&lt;br /&gt;
The following graphs show the average magnetisation and energy per spin as temperature increases from 0.25 with error bars. &lt;br /&gt;
[[File:By15172x2.png|centre|thumb|Figure 4 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 2x2 lattice]]&lt;br /&gt;
[[File:By15174x4.png|centre|thumb|Figure 5 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 4x4 lattice]]&lt;br /&gt;
[[File:By15178x8.png|centre|thumb|Figure 6 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 8x8 lattice]]&lt;br /&gt;
[[File:By151716x16.png|centre|thumb|Figure 7 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 16x16 lattice]]&lt;br /&gt;
[[File:By151732x32.png|centre|thumb|Figure 8 - Average magnetisation and energy per spin at a temperature range of 0.25 to 5.00 with dT=0.05 for a 32x32 lattice]]&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796449</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796449"/>
		<updated>2019-11-20T08:24:05Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 3: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 3 - Output of running ILanim.py script]]&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x32.png&amp;diff=796447</id>
		<title>File:By151732x32.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x32.png&amp;diff=796447"/>
		<updated>2019-11-20T08:22:30Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x16.png&amp;diff=796445</id>
		<title>File:By151716x16.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x16.png&amp;diff=796445"/>
		<updated>2019-11-20T08:22:11Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15178x8.png&amp;diff=796444</id>
		<title>File:By15178x8.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15178x8.png&amp;diff=796444"/>
		<updated>2019-11-20T08:22:10Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15174x4.png&amp;diff=796443</id>
		<title>File:By15174x4.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15174x4.png&amp;diff=796443"/>
		<updated>2019-11-20T08:22:09Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15172x2.png&amp;diff=796440</id>
		<title>File:By15172x2.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15172x2.png&amp;diff=796440"/>
		<updated>2019-11-20T08:20:59Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796439</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796439"/>
		<updated>2019-11-20T08:10:24Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 2x2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|300&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|1000&lt;br /&gt;
|15000&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|10000&lt;br /&gt;
|30000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|15000&lt;br /&gt;
|45000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796438</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796438"/>
		<updated>2019-11-20T08:01:11Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 2: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:By1517ILCheck.png|thumb|centre|Figure 2-Output of running ILCheck.py]]&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
[[File:By1517ILanim.png|centre|thumb|Figure 4 - Output of running ILanim.py script]]&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517ILanim.png&amp;diff=796437</id>
		<title>File:By1517ILanim.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517ILanim.png&amp;diff=796437"/>
		<updated>2019-11-20T07:59:48Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796421</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796421"/>
		<updated>2019-11-20T07:45:23Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* Task 2: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
[[File:By1517ILCheck.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517ILCheck.png&amp;diff=796419</id>
		<title>File:By1517ILCheck.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By1517ILCheck.png&amp;diff=796419"/>
		<updated>2019-11-20T07:44:44Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796410</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796410"/>
		<updated>2019-11-20T07:25:35Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This means that by plotting the heat capacity found with the above equation against temperature, the Curie Temperature can be found at the supposed peaks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796358</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796358"/>
		<updated>2019-11-20T01:24:28Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C=\frac{1}{Z}\frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)+\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\frac{d}{dT}\left( \frac{1}{Z} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796356</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796356"/>
		<updated>2019-11-20T01:23:00Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&amp;lt;E&amp;gt;=\frac{1}{Z}\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z is the partition function, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{1}{Z}=\frac{1}{\sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the product rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d(uv)=vdu+udv&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \frac{1}{Z} \right)=-\frac{1}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}\times \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{\left( \sum\limits_{\alpha }{{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =-\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; \frac{d}{dT}\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)=\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}\times \sum\limits_{\alpha }{\frac{E(\alpha )}{{{k}_{b}}{{T}^{2}}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
  &amp;amp; C=\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}+\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)\left( -\frac{1}{{{Z}^{2}}}\frac{\sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}} \right) \\ &lt;br /&gt;
 &amp;amp; =\frac{1}{Z}\times \frac{\sum\limits_{\alpha }{E{{(\alpha )}^{2}}{{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}}}{{{k}_{b}}{{T}^{2}}}-\frac{1}{{{Z}^{2}}}\frac{{{\left( \sum\limits_{\alpha }{E(\alpha ){{e}^{-\frac{E(\alpha )}{{{k}_{b}}T}}}} \right)}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{&amp;lt;{{E}^{2}}&amp;gt;-&amp;lt;E{{&amp;gt;}^{2}}}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
 &amp;amp; =\frac{Var[E]}{{{k}_{b}}{{T}^{2}}} \\ &lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796353</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796353"/>
		<updated>2019-11-20T01:21:09Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To carry out this differentiation, the product rule was used:&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039; Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796351</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796351"/>
		<updated>2019-11-20T01:17:15Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 7. Determining the heat capacity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: By definition,&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\partial \left\langle E\right\rangle}{\partial T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;From this, show that&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;C = \frac{\mathrm{Var}[E]}{k_B T^2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Where &amp;lt;math&amp;gt;\mathrm{Var}[E]&amp;lt;/math&amp;gt; is the variance in &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, &amp;lt;math&amp;gt;\mathrm{Var}[X]&amp;lt;/math&amp;gt;, the mean of its square &amp;lt;math&amp;gt;\left\langle X^2\right\rangle&amp;lt;/math&amp;gt;, and its squared mean &amp;lt;math&amp;gt;\left\langle X\right\rangle^2&amp;lt;/math&amp;gt;. You may find that the data around the peak is very noisy &amp;amp;mdash; this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796350</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796350"/>
		<updated>2019-11-20T01:16:58Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 7. Determining the heat capacity ==&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796270</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=796270"/>
		<updated>2019-11-19T23:21:09Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment.&#039;&#039;&#039;&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Run the ILcheck.py script from the IPython Qt console using the command&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;How many configurations are available to a system with 100 spins? To evaluate these expressions, we have to calculate the energy and magnetisation for each of these configurations, then perform the sum. Let&#039;s be very, very, generous, and say that we can analyse &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second with our computer. How long will it take to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed.&#039;&#039;&#039;&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;If &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, do you expect a spontaneous magnetisation (i.e. do you expect &amp;lt;math&amp;gt;\left\langle M\right\rangle \neq 0&amp;lt;/math&amp;gt;)? When the state of the simulation appears to stop changing (when you have reached an equilibrium state), use the controls to export the output to PNG and attach this to your report. You should also include the output from your statistics() function.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;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!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Task 3: ===&lt;br /&gt;
&#039;&#039;&#039;Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== Task 1: ===&lt;br /&gt;
&#039;&#039;&#039;The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Task 2: ===&lt;br /&gt;
&#039;&#039;&#039;Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from.&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795188</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795188"/>
		<updated>2019-11-14T15:37:13Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 2x2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy. ===&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so? ===&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero? ===&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment. ===&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Run the ILcheck.py script from the IPython Qt console using the command === &lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== 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;? ===&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed. ===&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== 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. ===&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 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!). ===&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure. ===&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|10&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|5&lt;br /&gt;
|150000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from. ===&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795185</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795185"/>
		<updated>2019-11-14T15:34:51Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 4x4 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy. ===&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so? ===&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero? ===&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment. ===&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Run the ILcheck.py script from the IPython Qt console using the command === &lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== 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;? ===&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed. ===&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== 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. ===&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 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!). ===&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure. ===&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15174x401.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15174x4025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15174x41.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15178x801.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15178x8025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15178x81.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151716x1601.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151716x16025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151716x161.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By151732x3201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By151732x32025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By151732x321.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from. ===&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795179</id>
		<title>Rep:Mod:2d ising by1517</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Mod:2d_ising_by1517&amp;diff=795179"/>
		<updated>2019-11-14T15:20:57Z</updated>

		<summary type="html">&lt;p&gt;By1517: /* 2x2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Third year CMP compulsory experiment =&lt;br /&gt;
&lt;br /&gt;
== 1. Introduction to the Ising model ==&lt;br /&gt;
&lt;br /&gt;
=== Show that the lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; is the number of dimensions and &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; is the total number of spins. What is the multiplicity of this state? Calculate its entropy. ===&lt;br /&gt;
&lt;br /&gt;
The interaction energy is defined as &amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By listing out the number of neighbours for each dimension, a relationship can be found.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Dimension&lt;br /&gt;
!Number of neighbours&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;&lt;br /&gt;
|&amp;lt;math&amp;gt;2\times D&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
For a ground state in the Ising model, all spins would be identical to minimize energy, so &amp;lt;math&amp;gt;s_i = s_j&amp;lt;/math&amp;gt; where &amp;lt;math&amp;gt;s_i = \pm 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Giving &amp;lt;math&amp;gt;s_i s_j = 1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Hence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;- \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} s_i s_j  =  - \frac{1}{2} J \sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Since the number of neighbours is equal to &amp;lt;math&amp;gt;2D&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The lowest possible energy for the Ising model is &amp;lt;math&amp;gt;E = - \frac{1}{2} J \sum_i^N (2D) = - \frac{1}{2} J (N) (2D) = -DNJ&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are 2 ways for the system to have identical spins, either all are spin up or all or spin down. This gives it a multiplicity of 2. So the entropy of this state is &amp;lt;math&amp;gt;S = k_b \ln 2 = 9.57 \times 10^{-24} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Imagine that the system is in the lowest energy configuration. To move to a different state, one of the spins must spontaneously change direction (&amp;quot;flip&amp;quot;). What is the change in energy if this happens (&amp;lt;math&amp;gt;D=3,\ N=1000&amp;lt;/math&amp;gt;)? How much entropy does the system gain by doing so? ===&lt;br /&gt;
&lt;br /&gt;
For the single flipped spin&amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;, spin&amp;lt;math&amp;gt;s_i s_j  = -1&amp;lt;/math&amp;gt;.  Then the change in interaction from -1 to 0 to account for the loss of stabilization energy is &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) = -2D&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The overall change in interaction energy is then &amp;lt;math&amp;gt;\sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2D(-1) + 2D(-1) = -4D&amp;lt;/math&amp;gt; to account for the additional gain in destabilization energy.&lt;br /&gt;
&lt;br /&gt;
The spin interaction of the system can be modelled as &amp;lt;math&amp;gt;\sum_i^N \sum_{j\  \in\  \mathrm{neighbours}\left(i\right)} = 2DN + 2 (-4D) &amp;lt;/math&amp;gt;, the destabilization energy is &#039;subtracted&#039; from the lowest energy state, the factor of 2 accounts for the double counting of the change in interaction. Essentially this also counts the change in interaction in terms of the neighbouring spins which see&#039;s the flipped spin, in addition to the change in interaction experienced by the flipped spin itself.&lt;br /&gt;
&lt;br /&gt;
The interaction energy is therefore &amp;lt;math&amp;gt;- \frac{1}{2} J (2DN + 2 (-4D)) = 4DJ-DNJ &amp;lt;/math&amp;gt;, then:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta E = 4DJ-DNJ - (-DNJ) =4DJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
With a dimension of 3,&amp;lt;math&amp;gt;\Delta E = 12J&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The multiplicity for this state is &amp;lt;math&amp;gt;\frac{1000!}{1!999!} = 1000&amp;lt;/math&amp;gt;, this is doubled to account for the opposite spin so &amp;lt;math&amp;gt;\Omega=2000&amp;lt;/math&amp;gt;. The entropy for this state is &amp;lt;math&amp;gt;S = k_b \ln 2000&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The increase in entropy is &amp;lt;math&amp;gt;\Delta S = k_b \ln 2000 - k_b \ln 2&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln \frac{2000}{2}&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = k_b \ln 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Delta S = 9.537 \times 10^{-23} J K^{-1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calculate the magnetisation of the 1D and 2D lattices in figure Fi1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero? ===&lt;br /&gt;
[[File:ThirdYearCMPExpt-IsingSketch.png|centre|thumb|&#039;&#039;&#039;Figure 1&#039;&#039;&#039; &amp;lt;ref&amp;gt;&amp;lt;nowiki&amp;gt;https://wiki.ch.ic.ac.uk/wiki/index.php?title=Third_year_CMP_compulsory_experiment/Introduction_to_the_Ising_model&amp;lt;/nowiki&amp;gt;&amp;lt;/ref&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The total magnetisation of the system is given by &amp;lt;math&amp;gt;M = \sum_i s_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For 1D lattice there are 3 spin ups and 2 spin downs,so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 3(1) + 2(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the 2D lattice, there are 13 spin ups and 12 spin downs, so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 13(1) + 12(-1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At absolute zero, the system adopt the lowest energy state where all spins are aligned, hence the magnetisation for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; would be &amp;lt;math&amp;gt;M = \pm 1000&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. Calculating the energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
=== Complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that &amp;lt;math&amp;gt;J=1.0&amp;lt;/math&amp;gt; at all times (in fact, we are working in &#039;&#039;reduced units&#039;&#039; in which &amp;lt;math&amp;gt;J=k_B&amp;lt;/math&amp;gt;, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment &amp;amp;mdash; we will address the speed in a later part of the experiment. ===&lt;br /&gt;
 import numpy as np&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 class IsingLattice:&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     E = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     E2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
     M2 = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     n_cycles = 0&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def __init__(self, n_rows, n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_rows = n_rows&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cols = n_cols&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice = np.random.choice([-1,1], size=(n_rows, n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def energy(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         energy = 0.0&amp;lt;br&amp;gt;&lt;br /&gt;
         for r in range(0,self.n_rows):&amp;lt;br&amp;gt;&lt;br /&gt;
             for c in range(0,self.n_cols):&amp;lt;br&amp;gt;&lt;br /&gt;
                 energy = energy - (self.lattice[r][c]*self.lattice[r][c-1]) - (self.lattice[r][c]*self.lattice[r-1][c])&amp;lt;br&amp;gt;&lt;br /&gt;
         return energy&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def magnetisation(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisation = np.sum(self.lattice)&amp;lt;br&amp;gt;&lt;br /&gt;
         return magnetisation&lt;br /&gt;
&lt;br /&gt;
=== Run the ILcheck.py script from the IPython Qt console using the command === &lt;br /&gt;
&lt;br /&gt;
== 3. Introduction to Monte Carlo simulation ==&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
=== 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;? ===&lt;br /&gt;
There are &amp;lt;math&amp;gt;2^{100}&amp;lt;/math&amp;gt; configurations in a system with 100 spins. If the analysis speed is &amp;lt;math&amp;gt;1\times 10^9&amp;lt;/math&amp;gt; configurations per second, then it would take &amp;lt;math&amp;gt;1.27 \times 10^{21}&amp;lt;/math&amp;gt; seconds to evaluate a single value of &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt;, or about 40.2 trillion years! This is about 2900 times longer than the age of the universe!&lt;br /&gt;
&lt;br /&gt;
=== Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of &amp;lt;math&amp;gt;k_B&amp;lt;/math&amp;gt;! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;math&amp;gt;&amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;&amp;lt;/math&amp;gt;, and the number of Monte Carlo steps that have elapsed. ===&lt;br /&gt;
     def montecarlostep(self, T):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it performs a single Monte Carlo step&amp;lt;br&amp;gt;&lt;br /&gt;
         initialenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         energyoutput = 0&amp;lt;br&amp;gt;&lt;br /&gt;
         #the following two lines will select the coordinates of the random spin for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_i = np.random.choice(range(0, self.n_rows))&amp;lt;br&amp;gt;&lt;br /&gt;
         random_j = np.random.choice(range(0, self.n_cols))&amp;lt;br&amp;gt;&lt;br /&gt;
         self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
         newenergy = self.energy()&amp;lt;br&amp;gt;&lt;br /&gt;
         deltaE = newenergy - initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         #the following line will choose a random number in the rang e[0,1) for you&amp;lt;br&amp;gt;&lt;br /&gt;
         random_number = np.random.random()&amp;lt;br&amp;gt;&lt;br /&gt;
         if deltaE &amp;gt; 0:&amp;lt;br&amp;gt;&lt;br /&gt;
             if random_number &amp;gt; np.exp(-deltaE/T):&amp;lt;br&amp;gt;&lt;br /&gt;
                 self.lattice[random_i][random_j]=-self.lattice[random_i][random_j]&amp;lt;br&amp;gt;&lt;br /&gt;
                 energyoutput = initialenergy&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             energyoutput = newenergy&amp;lt;br&amp;gt;&lt;br /&gt;
                 &lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
         magnetisationoutput = self.magnetisation()&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E = self.E + energyoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.E2 = self.E**2 + energyoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M = self.M + magnetisationoutput&amp;lt;br&amp;gt;&lt;br /&gt;
         self.M2 = self.M**2 + magnetisationoutput**2&amp;lt;br&amp;gt;&lt;br /&gt;
         self.n_cycles = self.n_cycles + 1&amp;lt;br&amp;gt;&lt;br /&gt;
         return (energyoutput,magnetisationoutput)&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
     def statistics(self):&amp;lt;br&amp;gt;&lt;br /&gt;
         # complete this function so that it calculates the correct values for the averages of E, E*E (E2), M, M*M (M2), and returns them&amp;lt;br&amp;gt;&lt;br /&gt;
         if self.n_cycles &amp;gt; 0: #prevents division by 0 &amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2/self.n_cycles&amp;lt;br&amp;gt;&lt;br /&gt;
         else:&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE=self.E&amp;lt;br&amp;gt;&lt;br /&gt;
             averageE2=self.E2&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM=self.M&amp;lt;br&amp;gt;&lt;br /&gt;
             averageM2=self.M2&amp;lt;br&amp;gt;&lt;br /&gt;
         return (averageE,averageE2,averageM,averageM2,self.n_cycles)&lt;br /&gt;
Averaged quantities:&lt;br /&gt;
E =  -1.519167450611477&lt;br /&gt;
E*E =  2453.2692997412983&lt;br /&gt;
M =  0.6334960018814676&lt;br /&gt;
M*M =  426.60110775076436&lt;br /&gt;
&lt;br /&gt;
=== 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. ===&lt;br /&gt;
&lt;br /&gt;
The Curie Temperature &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; is defined as the transition temperature between a ferromagnetic state of a material to a paramagnetic state. Spontaneous magnetisation decreases as the temperature increases from 0 to &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt; where spontaneous magnetisation becomes 0.&amp;lt;ref&amp;gt;Hook, J. R., and H. E. Hall. &#039;&#039;Solid State Physics&#039;&#039;, John Wiley &lt;br /&gt;
&amp;amp; Sons, Incorporated, 1991. ProQuest Ebook Central, &lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://ebookcentral.proquest.com/lib/imperial/detail.action?docID=1212553&amp;lt;/nowiki&amp;gt;.&amp;lt;/ref&amp;gt; Below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;, the system will eventually reach a ferromagnetic equilibrium state, this alighment of permanent dipole moments represents an increase in the degreee of order within the system which is associated witha decrease in entropy. This is because at low temperatures the interactions between permanenet dipoles is significant and without sufficient thermal energy to to cause dipoles to point in random directions in zero applied field, the dipoles align themselves as a way to minimize interaction energy, that is to say that the interaction energy stabililzation dominates at low temperatures below &amp;lt;math&amp;gt;T_C&amp;lt;/math&amp;gt;while above it the effect of entropy dominates as a way to minimize free energy.&lt;br /&gt;
&lt;br /&gt;
== 4. Accelerating the code ==&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;current&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the first version of IsingLattice.py&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard deviation (s)&lt;br /&gt;
|-&lt;br /&gt;
|3.097&lt;br /&gt;
|3.023&lt;br /&gt;
|3.058&lt;br /&gt;
|3.002&lt;br /&gt;
|2.973&lt;br /&gt;
|3.155&lt;br /&gt;
|3.051&lt;br /&gt;
|0.490&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 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!). ===&lt;br /&gt;
&lt;br /&gt;
=== Use the script ILtimetrial.py to record how long your &#039;&#039;new&#039;&#039;  version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This  will vary, depending on what else the computer happens to be doing, so  perform repeats and report the error in your average! ===&lt;br /&gt;
Time trials for the second version of IsingLattice.py (Implentation of np.roll and np.sum function)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.228&lt;br /&gt;
|0.226&lt;br /&gt;
|0.228&lt;br /&gt;
|0.223&lt;br /&gt;
|0.231&lt;br /&gt;
|0.229&lt;br /&gt;
|0.228&lt;br /&gt;
|0.002&lt;br /&gt;
|}&lt;br /&gt;
Time trials for the third version of IsingLattice.py (Adding the energy and magnetisation values as attributes of the lattice, this prevents repeat calculations in monte carlo steps)&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Trial 1 (s)&lt;br /&gt;
!Trial 2 (s)&lt;br /&gt;
!Trial 3 (s)&lt;br /&gt;
!Trial 4 (s)&lt;br /&gt;
!Trial 5 (s)&lt;br /&gt;
!Trial 6 (s)&lt;br /&gt;
!Average Time (s)&lt;br /&gt;
!Standard error of the mean (s)&lt;br /&gt;
|-&lt;br /&gt;
|0.134&lt;br /&gt;
|0.133&lt;br /&gt;
|0.133&lt;br /&gt;
|0.141&lt;br /&gt;
|0.139&lt;br /&gt;
|0.136&lt;br /&gt;
|0.136&lt;br /&gt;
|0.003&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 5. The effect of temperature ==&lt;br /&gt;
&lt;br /&gt;
=== The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the &#039;&#039;final&#039;&#039; lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure. ===&lt;br /&gt;
For future investigations, the cut off point for different lattice sizes were determined. The lattice sizes chosen are 2x2, 4x4, 8x8, 16x16, 32x32. Lower temperatures and larger lattices lead to an increase of the number of cycles before the equilibrium state is reached, to minimize the inclusion of these cycles in averages and maintain consistency.&lt;br /&gt;
&lt;br /&gt;
==== 2x2 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|[[File:By15172x201.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|[[File:By15172x2025.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|[[File:By15172x21.png|thumb]]&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 4x4 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 8x8 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==== 16x16 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== 32x32 ====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Temperature&lt;br /&gt;
!Graph&lt;br /&gt;
!Cutoff point&lt;br /&gt;
!Cycles&lt;br /&gt;
|-&lt;br /&gt;
|0.1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|0.25&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, &#039;&#039;with error bars&#039;&#039;, for an &amp;lt;math&amp;gt;8\times 8&amp;lt;/math&amp;gt; lattice. Use your 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 &amp;amp;mdash; you will need it later. Save the file as &#039;&#039;8x8.dat&#039;&#039; so that you know which lattice size it came from. ===&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x32025.png&amp;diff=795178</id>
		<title>File:By151732x32025.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x32025.png&amp;diff=795178"/>
		<updated>2019-11-14T15:18:56Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x321.png&amp;diff=795177</id>
		<title>File:By151732x321.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x321.png&amp;diff=795177"/>
		<updated>2019-11-14T15:18:55Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x3201.png&amp;diff=795176</id>
		<title>File:By151732x3201.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151732x3201.png&amp;diff=795176"/>
		<updated>2019-11-14T15:18:55Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x161.png&amp;diff=795175</id>
		<title>File:By151716x161.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x161.png&amp;diff=795175"/>
		<updated>2019-11-14T15:18:20Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x16025.png&amp;diff=795174</id>
		<title>File:By151716x16025.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x16025.png&amp;diff=795174"/>
		<updated>2019-11-14T15:18:20Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x1601.png&amp;diff=795173</id>
		<title>File:By151716x1601.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By151716x1601.png&amp;diff=795173"/>
		<updated>2019-11-14T15:18:19Z</updated>

		<summary type="html">&lt;p&gt;By1517: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>By1517</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15178x8025.png&amp;diff=795172</id>
		<title>File:By15178x8025.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:By15178x8025.png&amp;diff=795172"/>
		<updated>2019-11-14T15:17:45Z</updated>

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