<?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=Sb1016</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=Sb1016"/>
	<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/wiki/Special:Contributions/Sb1016"/>
	<updated>2026-04-05T19:18:15Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Liquidsim_sb1016&amp;diff=737277</id>
		<title>Rep:Liquidsim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:Liquidsim_sb1016&amp;diff=737277"/>
		<updated>2018-12-10T11:14:44Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: Created blank page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737164</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737164"/>
		<updated>2018-11-21T18:33:06Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|300px|thumb|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of functions&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|300px|thumb|Fig24.Polynomial fitting of the peak for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
[[File:curietempsb1016.png|250px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
However, from the graph it can be seen that the linear fitting is not very good as the data does not show a strong linear correlation. Repeats must be taken to improve fitting.&lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737163</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737163"/>
		<updated>2018-11-21T18:32:03Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|300px|thumb|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|300px|thumb|Fig24.Polynomial fitting of the peak for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
[[File:curietempsb1016.png|250px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
However, from the graph it can be seen that the linear fitting is not very good as the data does not show a strong linear correlation. Repeats must be taken to improve fitting.&lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737162</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737162"/>
		<updated>2018-11-21T18:29:11Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|300px|thumb|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|300px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
[[File:curietempsb1016.png|250px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
However, from the graph it can be seen that the linear fitting is not very good as the data does not show a strong linear correlation. Repeats must be taken to improve fitting.&lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737161</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737161"/>
		<updated>2018-11-21T18:27:40Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|300px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
[[File:curietempsb1016.png|250px|thumb|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
However, from the graph it can be seen that the linear fitting is not very good as the data does not show a strong linear correlation. Repeats must be taken to improve fitting.&lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737160</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737160"/>
		<updated>2018-11-21T18:25:51Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
However, from the graph it can be seen that the linear fitting is not very good as the data does not show a strong linear correlation. Repeats must be taken to improve fitting.&lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737159</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737159"/>
		<updated>2018-11-21T18:24:09Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; ,  to be distance dependent to model the actual force field (like Lennard-Jones potential) in a metal or crystal.  &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;br /&gt;
# Markov Processes, Gillespie, 4&amp;lt;sup&amp;gt;th&amp;lt;/sup&amp;gt;edn&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737156</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737156"/>
		<updated>2018-11-21T18:18:53Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
The aim of this computational experiment is to understand the ferromagnetic behaviour using Monte Carlo simulation of a 2D Ising model. The heat capacity and the Curie temperature of the system is also investigated. &lt;br /&gt;
&lt;br /&gt;
Ising model is a mathematical model used in statistical mechanics to depict ferromagnetism. In this model, the atomic spins of either +1 or -1 are arranged in a lattice and the spins are allowed to interact with its neighbours. As the temperature increases from 0K, the system moves from a parallel spin configuration (with a magnetisation of ±N, where N is the number of atoms in the lattice) to an anti-parallel spin configuration with 0 magnetisation. This phenomenon is also known as a phase transition and can be studied using the Monte Carlo simulation. &lt;br /&gt;
&lt;br /&gt;
During this experiment, the Monte Carlo simulation calculates the numerical averages of energies and magnetisation through computational algorithm and random sampling. &lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to Monte Carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days. Since it takes so long to compute a single value of magnetisation, Ising model cannot be directly used to calculate energy and magnetisation.   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;br /&gt;
In conclusion, the Monte Carlo simulation of the Ising model even at smaller lattice sizes provides a accurate and reliable depiction of ferromagnetism and phase transition. The phase transition is observed at T=2.288 which is in agreement with the analytical data, with only a small percentage difference. &lt;br /&gt;
&lt;br /&gt;
Further experiments can be also be carried out to better test the efficacy of Monte Carlo simulation and the Ising Model:&lt;br /&gt;
* The simulation can be carried out in 3D, using a 3D Ising model to predict phase transition in 3D structures such as crystals and metals.&lt;br /&gt;
* Can modify the force coupling parameter , J ,  to be distance dependent to model the actual force field in a metal or crystal. &lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
# Onsager, Lars (1944), &amp;quot;Crystal statistics. I. A two-dimensional model with an order-disorder transition&amp;quot;, Phys. Rev. (2) 65(3–4): 117–149&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737154</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737154"/>
		<updated>2018-11-21T17:10:43Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737153</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737153"/>
		<updated>2018-11-21T17:09:20Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|thumb|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|500px|thumb|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737152</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737152"/>
		<updated>2018-11-21T17:08:05Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are 2&amp;lt;sup&amp;gt;100 &amp;lt;/sup&amp;gt;configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take 1.467x10&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt; days.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737151</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737151"/>
		<updated>2018-11-21T17:05:29Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by &amp;lt;math&amp;gt;E\ =\ -DNJ&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^(100)&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^(16)&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|C++ data and python data are very similar. However the python data has more fluctuations this maybe due to the fact that the runtime in the python simulation was smaller than C++, thereby producing more fluctuations.&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|Magnetisation in C++ data goes to zero before python data. This might be due to smaller temperature spacing in the C++ run which accurately captures the critical region causing the magnetisation to quickly drop to 0. &lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|C++ Heat Capacities produce a narrower peak with lesser fluctuations than python. This can again be attributed to runtime and temperature spacing.&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737138</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737138"/>
		<updated>2018-11-21T15:29:51Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Monte Carlo simulation using Ising Model =&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2. Therefore, the lowest possible energy for the Ising model is given by .......&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -1, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation is observed. Therefore the lattice can either have all spins up or spins down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with alternating anti-parallel spins. This shows the effect of increasing temperature.   &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spin configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737100</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737100"/>
		<updated>2018-11-21T11:49:59Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
As lattice size increases, the graph becomes narrower and noisier around the critical region. The maximum heat capacity value increases with increasing lattice size from approximately 0.1 to 1.5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|Comment&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit &amp;amp;mdash; in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#first we fit the polynomial to the data&lt;br /&gt;
fit = np.polyfit(T4, H4, 9) # fit a third order polynomial&lt;br /&gt;
&lt;br /&gt;
#now we generate interpolated values of the fitted polynomial over the range of our function&lt;br /&gt;
T4_min = np.min(T4)&lt;br /&gt;
T4_max = np.max(T4)&lt;br /&gt;
T_range4 = np.linspace(T4_min, T4_max, 10000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
fitted_C4_values = np.polyval(fit, T_range4) # use the fit object to generate the corresponding values of C&lt;br /&gt;
plot(T4, H4 , label = &amp;quot;Initial Heat Capacity&amp;quot;)&lt;br /&gt;
plot(T_range4, fitted_C4_values, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
legend()&lt;br /&gt;
title(&amp;quot;Fitting of Heat Capacity for 4X4 lattice&amp;quot;)&lt;br /&gt;
xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is difficult to get a good fit across the whole plot as the plot is not strictly polynomial. The degree of polynomial fitting was increases to very high values such as 9 and 11 to get a good fitting throughout the plot. However because of this high degree, the fitting may not be reliable over the whole region and cannot be used to extrapolate data. &lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
The fitting was limited to the region between T=2.0 and T=3.0. This fitting gives a good plot and reliable data in the critical region. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def fitting_HC(T2,H2,T2_min,T2_max,n):&lt;br /&gt;
    new_temp=[]&lt;br /&gt;
    new_C=[]&lt;br /&gt;
    T2_min = 2 &lt;br /&gt;
    T2_max = 3 &lt;br /&gt;
    i=0&lt;br /&gt;
&lt;br /&gt;
    min_temp_selection = np.logical_and(T2 &amp;lt; T2_min, i==i )&lt;br /&gt;
    min_T2_values = T2[min_temp_selection]&lt;br /&gt;
    min_H2_values = H2[min_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(min_T2_values)&lt;br /&gt;
    new_C=new_C+list(min_H2_values)&lt;br /&gt;
&lt;br /&gt;
    selection = np.logical_and(T2 &amp;gt; T2_min, T2 &amp;lt; T2_max) #choose only those rows where both conditions are true&lt;br /&gt;
    peak_T2_values = T2[selection]&lt;br /&gt;
    peak_H2_values = H2[selection]&lt;br /&gt;
&lt;br /&gt;
    fit = np.polyfit(peak_T2_values, peak_H2_values, 3) &lt;br /&gt;
&lt;br /&gt;
    T_range2 = np.linspace(T2_min, T2_max, 1000) #generate 1000 evenly spaced points between T_min and T_max&lt;br /&gt;
    fitted_C2_values = np.polyval(fit, T_range2) # use the fit objectto generate the corresponding values of C&lt;br /&gt;
&lt;br /&gt;
    new_temp=new_temp+list(T_range2)&lt;br /&gt;
    new_C=new_C+list(fitted_C2_values)&lt;br /&gt;
&lt;br /&gt;
    max_temp_selection = np.logical_and(T2 &amp;gt; T2_max,i==i)&lt;br /&gt;
    max_T2_values = T2[max_temp_selection]&lt;br /&gt;
    max_H2_values = H2[max_temp_selection]&lt;br /&gt;
    new_temp=new_temp+list(max_T2_values)&lt;br /&gt;
    new_C=new_C+list(max_H2_values)&lt;br /&gt;
    &lt;br /&gt;
    plot(new_temp, new_C, label = &amp;quot;Fitted Heat Capacity&amp;quot;)&lt;br /&gt;
    legend()&lt;br /&gt;
    xlabel(&amp;quot;Temperature&amp;quot;)&lt;br /&gt;
    ylabel(&amp;quot;Heat Capacity&amp;quot;)&lt;br /&gt;
    title(&amp;quot;Fitting of Heat Capacity for &amp;quot;+str(n)+&#039; lattice&#039;)&lt;br /&gt;
    return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; was calculated to be 2.288&amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The literature &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; is 2.269 &amp;lt;math&amp;gt;J/k_b&amp;lt;/math&amp;gt;. The percentage difference is 0.837% which is extremely small. This shows that the fitting is very good and that the results by comparing relatively smaller lattices still give a reliable value for &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The major source of error is from using a maximum lattice size of 32x32 which is still small to compute &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;. The smaller lattices do not accurately depict the infinite lattice as the number of configurations in a 32x32 lattice is much smaller. Computing this simulation for larger lattices will improve the accuracy of &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt;.&lt;br /&gt;
Moreover, In ILtemperaturerange.py, the runtime for each lattice size can be increased to further reduce the error (standard deviation) in the computed energy and magnetisation values, which will in turn reduce the error in the &amp;lt;math&amp;gt;T_{C,\infty}&amp;lt;/math&amp;gt; calculated. &lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737065</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737065"/>
		<updated>2018-11-21T11:17:46Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|500px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737064</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737064"/>
		<updated>2018-11-21T11:17:15Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737063</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737063"/>
		<updated>2018-11-21T11:16:31Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr8sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr8sb1016.png&amp;diff=737062</id>
		<title>File:Dr8sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr8sb1016.png&amp;diff=737062"/>
		<updated>2018-11-21T11:16:14Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737061</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737061"/>
		<updated>2018-11-21T11:14:32Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;:  [[File:Dr2sb1016.png|250px|Fig16.]] , where  [[File:Dr3sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;E&amp;gt; is differentiated with respect to T, we obtain the following equation:&lt;br /&gt;
 [[File:Dr4sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
Upon further simplification:  [[File:Dr5sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Also, [[File:Dr6sb1016.png|250px|Fig16.]].&lt;br /&gt;
&lt;br /&gt;
Therefore, [[File:Dr7sb1016.png|250px|Fig16.]].&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr7sb1016.png&amp;diff=737060</id>
		<title>File:Dr7sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr7sb1016.png&amp;diff=737060"/>
		<updated>2018-11-21T11:14:22Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr6sb1016.png&amp;diff=737059</id>
		<title>File:Dr6sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr6sb1016.png&amp;diff=737059"/>
		<updated>2018-11-21T11:13:02Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr5sb1016.png&amp;diff=737058</id>
		<title>File:Dr5sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr5sb1016.png&amp;diff=737058"/>
		<updated>2018-11-21T11:11:27Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr4sb1016.png&amp;diff=737055</id>
		<title>File:Dr4sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr4sb1016.png&amp;diff=737055"/>
		<updated>2018-11-21T11:09:06Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737053</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737053"/>
		<updated>2018-11-21T11:07:14Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy or the ensemble energy average is given by the sum of the microstate energy &amp;lt;math&amp;gt;E_i&amp;lt;/math&amp;gt; times its probability &amp;lt;math&amp;gt;P_i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr1sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
The previous equation can therefore be written in terms of the partition function &amp;lt;math&amp;gt;Z&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 [[File:Dr2sb1016.png|250px|Fig16.]] &lt;br /&gt;
&lt;br /&gt;
where  [[File:Dr3sb1016.png|250px|Fig16.]]. &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;
&#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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr3sb1016.png&amp;diff=737052</id>
		<title>File:Dr3sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr3sb1016.png&amp;diff=737052"/>
		<updated>2018-11-21T11:07:05Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr2sb1016.png&amp;diff=737040</id>
		<title>File:Dr2sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr2sb1016.png&amp;diff=737040"/>
		<updated>2018-11-21T11:01:18Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737034</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737034"/>
		<updated>2018-11-21T10:59:26Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy can be calculated by:&lt;br /&gt;
[[File:Dr1sb1016.png|250px|Fig16.]]&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr1sb1016.png&amp;diff=737029</id>
		<title>File:Dr1sb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Dr1sb1016.png&amp;diff=737029"/>
		<updated>2018-11-21T10:57:41Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737017</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737017"/>
		<updated>2018-11-21T10:48:16Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;
Expectation of energy can be calculated by &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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737002</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=737002"/>
		<updated>2018-11-21T10:21:41Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results with error bars]]&lt;br /&gt;
[[File:Errorsb1016.png|250px|alt=Alt text|Fig6. Error bars after zooming in on Fig 5.]]&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible. Due to this, the error bars being plotted (using standard error) are extremely small and cannot be seen without zooming in. &lt;br /&gt;
&lt;br /&gt;
As temperature increases, energy/spin remains relatively constant at -2.0 till the critical region. Near &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt; (between T=2.0 and T=3.0), energy/spin increases to 0.0 and remains relatively constant after. &lt;br /&gt;
&lt;br /&gt;
The initial magnetisation is +1 with all parallel spins. As temperature increases close to the &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, the entropic driving force takes over and cause spin flipping to maximise entropy. This creates an anti-parallel spin configuration with equal and alternating spin ups and downs, giving 0 magnetisation. &lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As the lattice size increases, long range fluctuations decrease. 16x16 is large enough to capture these fluctuations. In the critical region around &amp;lt;math&amp;gt;T_c&amp;lt;/math&amp;gt;, a single change in spin can have a large impact on magnetisation. In a smaller lattice, the impact of this single spin is greater due to the small number of configurations available for spin flipping. However in a larger lattice, this impact is smaller and therefore fluctuations are also much smaller. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=File:Errorsb1016.png&amp;diff=736993</id>
		<title>File:Errorsb1016.png</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=File:Errorsb1016.png&amp;diff=736993"/>
		<updated>2018-11-21T10:10:52Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736952</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736952"/>
		<updated>2018-11-21T09:46:39Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736950</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736950"/>
		<updated>2018-11-21T09:45:47Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250pxFig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736947</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736947"/>
		<updated>2018-11-21T09:42:00Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|400px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
With the new code, the average time for Monte Carlo steps to run has decreased by 8.73s. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|thumb|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|thumb|250pxFig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736932</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736932"/>
		<updated>2018-11-21T09:30:56Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|Fig 1.The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The result from ILcheck.py in Fig 1. shows a 4x4 lattices from a low energy state with all parallel spins to a high energy state with anti-parallel spins.  &lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|thumb|500px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736930</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736930"/>
		<updated>2018-11-21T09:25:52Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736929</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736929"/>
		<updated>2018-11-21T09:24:19Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|left|500px|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736928</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736928"/>
		<updated>2018-11-21T09:23:49Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736927</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736927"/>
		<updated>2018-11-21T09:23:09Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|thumb|500px|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736926</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736926"/>
		<updated>2018-11-21T09:22:01Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb016.PNG|thumb|500px|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736924</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736924"/>
		<updated>2018-11-21T09:21:10Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
[[File:ILcheckssb016.PNG|thumb|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736917</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736917"/>
		<updated>2018-11-21T09:14:46Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Results from montecarlostep() for a 8x8 lattice at T=1.0: (8.0, 2.0)&lt;br /&gt;
&lt;br /&gt;
Results from the statistics() function for a 8x8 lattice at T=1.0 : (8.0, 64.0, 2.0, 4.0, 1)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736911</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736911"/>
		<updated>2018-11-21T09:07:08Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== Calculating energy and magnetisation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==Introduction to monte carlo simulation==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==Accelerating the code==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==The effect of temperature==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
==The effect of system size==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy &#039;&#039;per spin&#039;&#039; versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Determining heat capacity==&lt;br /&gt;
&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;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==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;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&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 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;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&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;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&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;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Conclusion==&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736903</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736903"/>
		<updated>2018-11-21T09:01:33Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|-&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736897</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736897"/>
		<updated>2018-11-21T08:58:33Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|alt=Alt text|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|alt=Alt text|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|alt=Alt text|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|alt=Alt text|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|alt=Alt text|Fig5. 8x8 Lattice results]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|-&lt;br /&gt;
|2x2&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|-&lt;br /&gt;
|4x4&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|-&lt;br /&gt;
|8x8&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|16x16&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|-&lt;br /&gt;
|32x32&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736890</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736890"/>
		<updated>2018-11-21T08:50:14Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|500px|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|Fig5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736889</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736889"/>
		<updated>2018-11-21T08:44:49Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|250px|thumb|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel spin configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|thumb|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|250px|Fig3. 20x20 Lattice at T=0.0004]]  [[File:50501sb1016.png|250px|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|Fig5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736888</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736888"/>
		<updated>2018-11-21T08:41:36Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is &amp;lt;math&amp;gt;K_b ln(1000)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Run the ILcheck.py script from the IPython Qt console using the command &amp;lt;pre&amp;gt;%run ILcheck.py&amp;lt;/pre&amp;gt; The displayed window has a series of control buttons in the bottom left, one of which will allow you to export the figure as a PNG image. Save an image of the ILcheck.py output, and include it in your report.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|250px|thumb|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&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;: 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;
&amp;lt;math&amp;gt;F = U - TS&amp;lt;/math&amp;gt;, where &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; is the Helmholtz free energy, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; is the internal energy of the system, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; is temperature and &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; is entropy. &lt;br /&gt;
&lt;br /&gt;
At a given temperature, the system is controls the balance between the entropically favoured configuration and the energetically favoured configuration. When &amp;lt;math&amp;gt;T &amp;lt; T_C&amp;lt;/math&amp;gt;, the entropic driving force is not big enough to cause spins to flip to anti-parallel configuration. Therefore, a large number of parallel spins configuration and spontaneous magnetisation can be observed below the Curie temperature. &lt;br /&gt;
&lt;br /&gt;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|thumb|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Look at the documentation for the [http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html NumPy sum function]. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy [http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html roll] and [http://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html multiply] functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|Fig3. 20x20 Lattice at T=0.0004]]&lt;br /&gt;
&lt;br /&gt;
[[File:50501sb1016.png|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|Fig5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736883</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736883"/>
		<updated>2018-11-21T08:21:19Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Introduction to Ising Model ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, the energy of the system is -3000J. When a spin is flipped, firstly the system loses 6 favourable interactions, gaining 6J. Moreover, it then has 6 unfavourable interactions which also adds 6J. In total the energy of the system increases by 12J. &lt;br /&gt;
&lt;br /&gt;
In a system where N=1000, any of the 1000 spins can flip giving 1000 configurations. Therefore entropy is K_b ln(1000). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;gt;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
2D Lattice: +1 &lt;br /&gt;
&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039; ==&lt;br /&gt;
TASK: complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&lt;br /&gt;
&lt;br /&gt;
TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|250px|thumb|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
TASK: Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TASK: If T &amp;lt; T_C, do you expect a spontaneous magnetisation (i.e. do you expect \left\langle M\right\rangle \neq 0)? 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;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|thumb|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Use the script ILtimetrial.py to record how long your current version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: Look at the documentation for the NumPy sum function. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TASK: Use the script ILtimetrial.py to record how long your new version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|500px|thumb|Fig3. 20x20 Lattice at T=0.0004]]&lt;br /&gt;
[[File:50501sb1016.png|500px|thumb|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an 8\times 8 lattice. Use your intuition and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|Fig5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
	<entry>
		<id>https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736758</id>
		<title>Rep:MonteCarlosim sb1016</title>
		<link rel="alternate" type="text/html" href="https://chemwiki.ch.ic.ac.uk/index.php?title=Rep:MonteCarlosim_sb1016&amp;diff=736758"/>
		<updated>2018-11-21T02:06:05Z</updated>

		<summary type="html">&lt;p&gt;Sb1016: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Introduction&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the Ising model, the interaction energy is given by &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;
In one dimension, each atom/cell has two neighbours which repeats due to the periodicity of the lattice. &lt;br /&gt;
{| border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse:collapse&amp;quot;&lt;br /&gt;
|3&lt;br /&gt;
| 1&lt;br /&gt;
| 2&lt;br /&gt;
| 3&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
From the table above, cell 1 interacts with cell 3 and 2, whereas 2 interacts with 1 and 3. For the lowest energy configuration, the neighbouring cells will have the same spin. Therefore, the interaction energy is given by &amp;lt;math&amp;gt;- \frac{1}{2} J N( 2 s_i s_j ) &amp;lt;/math&amp;gt;, which is equal to &amp;lt;math&amp;gt;E\ =\ -NJ&amp;lt;/math&amp;gt; where D=1. &lt;br /&gt;
In 2D there are 4 interactions per cell and the lowest energy is given by  &amp;lt;math&amp;gt;E\ =\ -2NJ&amp;lt;/math&amp;gt; where D=2.&lt;br /&gt;
&lt;br /&gt;
In the lowest energy configuration, as all the cells have the same spin, it can be either +1 or -2, giving two possible states. The multiplicity is 2. Entropy is therefore &amp;lt;math&amp;gt;K_b ln2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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?&#039;&#039;&#039;&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;: Calculate the magnetisation of the 1D and 2D lattices in figure 1. What magnetisation would you expect to observe for an Ising lattice with &amp;lt;math&amp;gt;D = 3,\ N=1000&amp;lt;/math&amp;gt; at absolute zero?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1D Lattice: +1&lt;br /&gt;
2D Lattice: +1&lt;br /&gt;
For the 3D Ising Lattice with 1000 cells: At 0K, lowest possible energy configuration with total magnetisation. Therefore the lattice can either have all spin up or spin down. This will give a total magnetisation of ±1000. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Calculating energy and magnetisation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: complete the functions energy() and magnetisation(), which should return the energy of the lattice and the total magnetisation, respectively. In the energy() function you may assume that J=1.0 at all times (in fact, we are working in reduced units in which J=k_B, but there will be more information about this in later sections). Do not worry about the efficiency of the code at the moment — we will address the speed in a later part of the experiment.&lt;br /&gt;
&lt;br /&gt;
TASK: Run the ILcheck.py script from the IPython Qt console using the command&lt;br /&gt;
&lt;br /&gt;
[[File:ILchecksb1016.PNG|250px|thumb|Fig 1. The result from ILcheck.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Introduction to monte carlo simulation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;TASK&amp;lt;/big&amp;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;?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For a system with 100 spins, there are &amp;lt;math&amp;gt; 2^100&amp;lt;/math&amp;gt; configurations.  To evaluate a single &amp;lt;math&amp;gt;\left\langle M\right\rangle_T&amp;lt;/math&amp;gt; value, it would take &amp;lt;math&amp;gt; 1.467\times10^16&amp;lt;/math&amp;gt; days. &lt;br /&gt;
&lt;br /&gt;
TASK: Implement a single cycle of the above algorithm in the montecarlocycle(T) function. This function should return the energy of your lattice and the magnetisation at the end of the cycle. You may assume that the energy returned by your energy() function is in units of k_B! Complete the statistics() function. This should return the following quantities whenever it is called: &amp;lt;E&amp;gt;, &amp;lt;E^2&amp;gt;, &amp;lt;M&amp;gt;, &amp;lt;M^2&amp;gt;, and the number of Monte Carlo steps that have elapsed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TASK: If T &amp;lt; T_C, do you expect a spontaneous magnetisation (i.e. do you expect \left\langle M\right\rangle \neq 0)? 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;
From Fig2. it can be seen that after around 600 steps, the system seems to reach equilibrium.&lt;br /&gt;
&lt;br /&gt;
[[File:ILanimsb1016.png|500px|thumb|Fig2. The result from ILanim.py]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Accelerating the code&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Use the script ILtimetrial.py to record how long your current version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|9.646776585&lt;br /&gt;
|9.098013143&lt;br /&gt;
|9.025683231&lt;br /&gt;
|9.186795471&lt;br /&gt;
|9.219164678&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|9.235286621&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.242235881&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: Look at the documentation for the NumPy sum function. You should be able to modify your magnetisation() function so that it uses this to evaluate M. The energy is a little trickier. Familiarise yourself with the NumPy roll and multiply functions, and use these to replace your energy double loop (you will need to call roll and multiply twice!).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def energy(self):&lt;br /&gt;
        &amp;quot;Return the total energy of the current lattice configuration.&amp;quot;&lt;br /&gt;
        energy = -(np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,0))))))-&lt;br /&gt;
        (np.sum(np.multiply(self.lattice(np.roll(self.lattice(1,1))))))&lt;br /&gt;
        return energy&lt;br /&gt;
&lt;br /&gt;
    def magnetisation(self):&lt;br /&gt;
        &amp;quot;Return the total magnetisation of the current lattice configuration.&amp;quot;&lt;br /&gt;
        magnetisation=np.sum(self.lattice)&lt;br /&gt;
        return magnetisation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TASK: Use the script ILtimetrial.py to record how long your new version of IsingLattice.py takes to perform 2000 Monte Carlo steps. This will vary, depending on what else the computer happens to be doing, so perform repeats and report the error in your average!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Time Taken(s)&lt;br /&gt;
|0.577154774&lt;br /&gt;
|0.500357554&lt;br /&gt;
|0.493485749&lt;br /&gt;
|0.486324092&lt;br /&gt;
|0.485946439&lt;br /&gt;
|-&lt;br /&gt;
|Average time taken(s)&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.508653721&lt;br /&gt;
|-&lt;br /&gt;
|Standard Deviation&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot;|0.038747648&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: The script ILfinalframe.py runs for a given number of cycles at a given temperature, then plots a depiction of the final lattice state as well as graphs of the energy and magnetisation as a function of cycle number. This is much quicker than animating every frame! Experiment with different temperature and lattice sizes. How many cycles are typically needed for the system to go from its random starting position to the equilibrium state? Modify your statistics() and montecarlostep() functions so that the first N cycles of the simulation are ignored when calculating the averages. You should state in your report what period you chose to ignore, and include graphs from ILfinalframe.py to illustrate your motivation in choosing this figure.&lt;br /&gt;
&lt;br /&gt;
As lattice size and temperature of the system increases, the Monte Carlo steps needed for equilibrium increases. For a 2x2 lattice, at T=5.0, it reached equilibrium before 20,000 steps. However, a 32x32 lattice requires nearly 50,000 steps. &lt;br /&gt;
&lt;br /&gt;
[[File:20200.0004sb1016.png|500px|thumb|Fig3. 20x20 Lattice at T=0.0004]]&lt;br /&gt;
[[File:50501sb1016.png|500px|thumb|Fig4. 50x50 Lattice at T=1]]&lt;br /&gt;
&lt;br /&gt;
It can be seen from Fig 3. and Fig 4. that 20x20 lattice at a low temperature requires far lesser steps than 50x50 at a higher temperature. In order to not waste computational energy, the minimum number of steps was set to 20,000 without a upper limit. The montecarlostep function was modified so that the monetcarlostep result is only recorded of if the standard deviation is less than 5% of the mean. The code is further explained below. No change was made to the statistics function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def montecarlostep(self, T):&lt;br /&gt;
        energy_list=[]&lt;br /&gt;
        self.n_cycles = self.n_cycles + 1&lt;br /&gt;
        self.steps=self.steps+1&lt;br /&gt;
        energy = self.energy()&lt;br /&gt;
&lt;br /&gt;
        #First montecarlostep is run and the result is added to the energy_list&lt;br /&gt;
        random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
        random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
        self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_diff = self.energy() - energy&lt;br /&gt;
        random_number = np.random.random()&lt;br /&gt;
        if energy_diff &amp;gt; 0:&lt;br /&gt;
            if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
        energy_list=energy_list+[energy]&lt;br /&gt;
       &lt;br /&gt;
        #More values are added to the energy list by running the monetcarlostep in order to calculate standard deviation&lt;br /&gt;
        while (self.steps&amp;lt;20000):&lt;br /&gt;
            energy = self.energy()&lt;br /&gt;
            #&lt;br /&gt;
            random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
            random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_diff = self.energy() - energy&lt;br /&gt;
            random_number = np.random.random()&lt;br /&gt;
            #&lt;br /&gt;
            if energy_diff &amp;gt; 0:&lt;br /&gt;
                if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
            energy_list=energy_list+[energy]&lt;br /&gt;
            self.steps=self.steps+1&lt;br /&gt;
&lt;br /&gt;
       #To make sure the following step only runs the first time the IF condition is specified&lt;br /&gt;
        i=0&lt;br /&gt;
        if self.steps==20000:&lt;br /&gt;
            #Montecarlostep is run till the standard deviation of the energy list is less than 5% of the mean.&lt;br /&gt;
            while i==0:&lt;br /&gt;
                if np.std(energy_list)&amp;gt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    #&lt;br /&gt;
                    random_i = np.random.choice(range(0, self.n_rows))&lt;br /&gt;
                    random_j = np.random.choice(range(0, self.n_cols))&lt;br /&gt;
                    self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_diff = self.energy() - energy&lt;br /&gt;
                    random_number = np.random.random()&lt;br /&gt;
                    #this will choose a random number in the range[0,1)&lt;br /&gt;
                    if energy_diff &amp;gt; 0:&lt;br /&gt;
                        if random_number &amp;gt; np.exp(-energy_diff/T):&lt;br /&gt;
                            self.lattice[random_i,random_j] = -self.lattice[random_i,random_j]&lt;br /&gt;
                    energy_list=energy_list+[energy]&lt;br /&gt;
                    self.steps=self.steps+1&lt;br /&gt;
                if np.std(energy_list)&amp;lt;abs(0.05*np.mean(energy_list)):&lt;br /&gt;
                    i=1&lt;br /&gt;
       #The result from the last monte carlo step is recorded   &lt;br /&gt;
        self.E = self.E + self.energy()&lt;br /&gt;
        self.M = self.M + self.magnetisation()&lt;br /&gt;
        self.E2 = self.E2 + self.energy()**2&lt;br /&gt;
        self.M2 = self.M2 + self.magnetisation()**2   &lt;br /&gt;
        return(self.energy(), self.magnetisation())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TASK: Use ILtemperaturerange.py to plot the average energy and magnetisation for each temperature, with error bars, for an 8\times 8 lattice. Use your intuition and results from the script ILfinalframe.py to estimate how many cycles each simulation should be. The temperature range 0.25 to 5.0 is sufficient. Use as many temperature points as you feel necessary to illustrate the trend, but do not use a temperature spacing larger than 0.5. The NumPy function savetxt() stores your array of output data on disk — you will need it later. Save the file as 8x8.dat so that you know which lattice size it came from.&lt;br /&gt;
&lt;br /&gt;
Temperature spacing was set to 0.01 to effectively capture long range fluctuations. After the montecarlostep is run for the first time for the lattice at a specific temperature, it is already reached the equilibrium  value. The runtime was therefore set 100,000 cycles to make sure the average calculated has the smallest standard deviation and standard error possible.&lt;br /&gt;
&lt;br /&gt;
[[File:88tempsb1016.png|250px|Fig5.]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The effect of system size&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Repeat the final task of the previous section for the following lattice sizes: 2x2, 4x4, 8x8, 16x16, 32x32. Make sure that you name each datafile that your produce after the corresponding lattice size! Write a Python script to make a plot showing the energy per spin versus temperature for each of your lattice sizes. Hint: the NumPy loadtxt function is the reverse of the savetxt function, and can be used to read your previously saved files into the script. Repeat this for the magnetisation. As before, use the plot controls to save your a PNG image of your plot and attach this to the report. How big a lattice do you think is big enough to capture the long range fluctuations?&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:22energysb1016.png|250px|Fig6.]]&lt;br /&gt;
|[[File:44energysb1016.png|250px|Fig7.]]&lt;br /&gt;
|[[File:88energysb1016.png|250px|Fig8.]]&lt;br /&gt;
|[[File:1616energysb1016.png|250px|Fig9.]]&lt;br /&gt;
|[[File:3232energysb1016.png|250px|Fig10.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:22magsb1016.png|250px|Fig11.]]&lt;br /&gt;
|[[File:44magsb1016.png|250px|Fig12.]]&lt;br /&gt;
|[[File:88magsb1016.png|250px|Fig13.]]&lt;br /&gt;
|[[File:1616magsb1016.png|250px|Fig14.]]&lt;br /&gt;
|[[File:3232magsb1016.png|250px|Fig15.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;determining heat capacity&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: Write a Python script to make a plot showing the heat capacity versus temperature for each of your lattice sizes from the previous section. You may need to do some research to recall the connection between the variance of a variable, \mathrm{Var}[X], the mean of its square \left\langle X^2\right\rangle, and its squared mean \left\langle X\right\rangle^2. You may find that the data around the peak is very noisy — this is normal, and is a result of being in the critical region. As before, use the plot controls to save your a PNG image of your plot and attach this to the report.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|2x2&lt;br /&gt;
|4x4&lt;br /&gt;
|8x8&lt;br /&gt;
|16x16&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Heat capacity/spin vs Temperature&lt;br /&gt;
|[[File:22hcsb1016.png|250px|Fig16.]]&lt;br /&gt;
|[[File:44hcsb1016.png|250px|Fig17.]]&lt;br /&gt;
|[[File:88hcsb1016.png|250px|Fig18.]]&lt;br /&gt;
|[[File:1616hcsb1016.png|250px|Fig19.]]&lt;br /&gt;
|[[File:3232hcsb1016.png|250px|Fig20.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Locating the Curie temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TASK: A C++ program has been used to run some much longer simulations than would be possible on the college computers in Python. You can view its source code here if you are interested. Each file contains six columns: T, E, E^2, M, M^2, C (the final five quantities are per spin), and you can read them with the NumPy loadtxt function as before. For each lattice size, plot the C++ data against your data. For one lattice size, save a PNG of this comparison and add it to your report — add a legend to the graph to label which is which. To do this, you will need to pass the label=&amp;quot;...&amp;quot; keyword to the plot function, then call the legend() function of the axis object (documentation here).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|Lattice size&lt;br /&gt;
|32x32&lt;br /&gt;
|-&lt;br /&gt;
|Energy/spin vs Temperature&lt;br /&gt;
|[[File:3232Ecomparesb1016.png|250px|Fig21.]]&lt;br /&gt;
|-&lt;br /&gt;
|Magnetisation/spin vs Temperature&lt;br /&gt;
|[[File:3232Mcomparesb1016.png|250px|Fig22.]]&lt;br /&gt;
|-&lt;br /&gt;
|Heat Capacity/spin vs Temperature&lt;br /&gt;
|[[File:3232HCcomparesb1016.png|250px|Fig23.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
TASK: write a script to read the data from a particular file, and plot C vs T, as well as a fitted polynomial. Try changing the degree of the polynomial to improve the fit — in general, it might be difficult to get a good fit! Attach a PNG of an example fit to your report.&lt;br /&gt;
&lt;br /&gt;
[[File:441fittingsb1016.png|250px|Fig23. Initial polynomial fitting for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: Modify your script from the previous section. You should still plot the whole temperature range, but fit the polynomial only to the peak of the heat capacity! You should find it easier to get a good fit when restricted to this region.&lt;br /&gt;
&lt;br /&gt;
[[File:442fittingsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
TASK: find the temperature at which the maximum in C occurs for each datafile that you were given. Make a text file containing two colums: the lattice side length (2,4,8, etc.), and the temperature at which C is a maximum. This is your estimate of T_C for that side length. Make a plot that uses the scaling relation given above to determine T_{C,\infty}. By doing a little research online, you should be able to find the theoretical exact Curie temperature for the infinite 2D Ising lattice. How does your value compare to this? Are you surprised by how good/bad the agreement is? Attach a PNG of this final graph to your report, and discuss briefly what you think the major sources of error are in your estimate.&lt;br /&gt;
&lt;br /&gt;
[[File:curietempsb1016.png|250px|Fig23.Polynomial fitting of the peak for 4x4 lattice.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Conclusion&lt;/div&gt;</summary>
		<author><name>Sb1016</name></author>
	</entry>
</feed>