[comment]: <> (Written by Andrés Cárdenas)
[comment]: <> (July 2021)
[comment]: <> (www.fysixs.com)
[comment]: <> (andres.cardenas@gmail.com)

    **Cool Stuff in Physics**

  *Andrés Cárdenas, 2021*, www.fysixs.com

#        **The Ising Model**
##             *One-D Treatment*

---
 **⟹**  ***To get started please run the cell below.***  **⟸**

  Use me  to run a cell

---

```
#@title <font size = 3 face="Verdana" color=9467BD> <b>START HERE: Environment Setup 💻
#################################################
#################################################
#                                         IMPORTS
#################################################
#################################################
import subprocess
import numpy as np
import sympy as sp
import ipywidgets as widgets
import h5py
from scipy.constants import k
from IPython.display import clear_output
from scipy import stats
from google.colab import output
from IPython.display import Markdown as md
from IPython.display import IFrame
from ipywidgets import GridBox
from bokeh.io import output_notebook, curdoc
from bokeh.themes import Theme
from bokeh.events import DoubleTap
from bokeh.layouts import column, row, layout
from bokeh.models import CustomJS, Panel, Tabs, RangeTool, Spinner
from bokeh.models import Slider, TextInput, Select, Div, Button
from bokeh.models import PrintfTickFormatter, LinearAxis, LabelSet
from bokeh.models import Range1d, TapTool, NumericInput
from bokeh.models.widgets import DataTable, TableColumn, ScientificFormatter, NumberFormatter
from bokeh.plotting import ColumnDataSource, figure, show
from bokeh.transform import factor_cmap

output_notebook()
sp.init_printing(use_unicode=True)

#################################################
#################################################
#                    Definitions for the Notebook
#################################################
#################################################

# Plotting colors
plt_clr = {
      "func" : "royalblue",
      "pol"  : "coral",
      "func1": "royalblue",
      "func2": "seagreen",
      "fill" : "#0D70B9",
      "te"   : "#FFC300",
      "te_m" : "#FBFF00",
      "txt"  : "#91899B",
  }

plt_clr = ['rgb(95, 70, 144)',
 'rgb(29, 105, 150)',
 'rgb(56, 166, 165)',
 'rgb(15, 133, 84)',
 'rgb(115, 175, 72)',
 'rgb(237, 173, 8)',
 'rgb(225, 124, 5)',
 'rgb(204, 80, 62)',
 'rgb(148, 52, 110)',
 'rgb(111, 64, 112)',
 'rgb(102, 102, 102)']

# UI Colors
ui_clr = {
    "button" : "#462770",
    "slider" : "#8B78A6",
    "txt"  : "#91899B",
}

TOOLTIPS = [
            ("(x,y)", "($x, $y)"),
            ]

theme = Theme(json = {
    "attrs": {
        "Figure" : {
            "background_fill_color": "#20262B",
            "border_fill_color": "#15191C",
            "outline_line_color": "#E0E0E0",
            "outline_line_alpha": 0.25
        },

        "Grid": {
            "grid_line_color": "#E0E0E0",
            "grid_line_alpha": 0.25
        },

        "Axis": {
            #"fixed_location": 0,

            "major_tick_line_alpha": 0.8,
            "major_tick_line_color": "lightgrey",

            "minor_tick_line_alpha": 0.8,
            "minor_tick_line_color": "lightgrey",

            "axis_line_alpha": 0.8,
            "axis_line_color": ui_clr['slider'],

            "major_label_text_color": "lightgrey",
            "major_label_text_font": "Helvetica",
            "major_label_text_font_size": "8pt",

            "axis_label_standoff": 10,
            "axis_label_text_color": "#E0E0E0",
            "axis_label_text_font": "Helvetica",
            "axis_label_text_font_size": "1.25em",
            "axis_label_text_font_style": "normal"
        },

        "Legend": {
            "spacing": 8,
            "glyph_width": 15,

            "label_standoff": 8,
            "label_text_color": "#E0E0E0",
            "label_text_font": "Helvetica",
            "label_text_font_size": "1.025em",

            "border_line_alpha": 0,
            "background_fill_alpha": 0.25,
            "background_fill_color": "#20262B"
        },

        "ColorBar": {
            "title_text_color": "#E0E0E0",
            "title_text_font": "Helvetica",
            "title_text_font_size": "1.025em",
            "title_text_font_style": "normal",

            "major_label_text_color": "#E0E0E0",
            "major_label_text_font": "Helvetica",
            "major_label_text_font_size": "1.025em",

            "background_fill_color": "#15191C",
            "major_tick_line_alpha": 0,
            "bar_line_alpha": 0
        },

        "Title": {
            "text_color": "#E0E0E0",
            "text_font": "Helvetica",
            "text_font_size": "1.15em"
        }
    }
})

curdoc().theme = theme

# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
# Plotting Defs
# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

def plot_spins(data, idx):

  colors = [ "firebrick" if spin < 0 else "royalblue" for spin in data['spins'][idx,:] ]

  nspins = len(colors)

  cds = ColumnDataSource(data={'x':np.array(range(nspins)),
                               'top':np.ones(nspins),
                               'color':colors})

  p_title = f"Spins at step {int((idx+1)*data['params'][4])}"
  p = figure(plot_width=300, plot_height=100,
             title=p_title,
             toolbar_location=None, tools="")

  p.vbar(x='x', top='top', color='color', source=cds)

  p.xgrid.visible = False
  p.ygrid.visible = False
  p.xaxis.visible = False
  p.yaxis.visible = False

  return p

def plot_energy(data):

  p = figure(plot_width=600, plot_height=300,
             title=f"Energy @ T = {data['params'][2]}",
             tooltips = [("(step, E)", "($x, $y)")],
             tools = "box_zoom,reset", toolbar_location="right")

  p.xaxis.axis_label="Step"
  p.axis.axis_label_text_font_size = "10pt"

  x = np.array(range(0,int(data['params'][3]),int(data['params'][4])))
  cds   = ColumnDataSource(data={'x':x,
                               'y':data['energy'][()]
                               })

  p.line(x='x', y='y', source=cds, color=plt_clr[1], line_width = 2)

  return p

def plot_mag(data):

  M = data['mag'][()]

  count, bins = np.histogram(data['mag'][()],
                             bins=np.arange(min(M),
                                            max(M) + 1))
  vzeros = np.zeros(len(bins)-1)
  vmax = max(count)*1.1

  # cds = ColumnDataSource(data={'x':bins[:-1],
                              #  'top':count})

  hist = figure(plot_width=200, plot_height=300,
                x_range=(0, vmax),
                title='Histogram',
                tooltips = [("(Mag, count)", "($y, $x)")],
                tools = "box_zoom,reset", toolbar_location=None,
                y_axis_location="right", min_border=10)

  hist.yaxis.axis_label="Magnetization"
  hist.xaxis.axis_label="Count"
  hist.axis.axis_label_text_font_size = "10pt"

  hist.quad(left=0, bottom=bins[:-1], top=bins[1:],
          right=count, color=plt_clr[0])

  hist.ygrid.visible = False
  hist.xaxis.major_label_orientation = np.pi/4
  hist.js_on_event(DoubleTap,
                   CustomJS(args=dict(p=hist), code='p.reset.emit()'))

  p = figure(plot_width=600, plot_height=300,
             title=f"Magnetization @ T = {data['params'][2]}",
             tooltips = [("(step, Mag)", "($x, $y)")],
             tools = "box_zoom,reset", toolbar_location="above")

  p.xgrid.visible = False
  p.xaxis.axis_label="Step"
  p.axis.axis_label_text_font_size = "10pt"

  x = np.array(range(0,int(data['params'][3]),int(data['params'][4])))
  cds   = ColumnDataSource(data={'x':x,
                               'y':data['mag'][()]
                               })

  p.line(x='x', y='y', source=cds, color=plt_clr[4])

  return row([p, hist])

def plot_corr(data):
  correl = data['corr'][()]
  nspins = len(data['spins'][0,:])

  p = figure(plot_width=600, plot_height=300,
            #  title=f'Correlation Function',
             tooltips = [("(r, corr)", "($x, $y)")],
             tools = "box_zoom,reset", toolbar_location="right")

  p.xaxis.axis_label = "Number of Spins Away"
  p.yaxis.axis_label = "Correlation"
  p.axis.axis_label_text_font_size = "10pt"

  r = np.arange((nspins-1)//2)

  p.line(x=r, y=correl, color=plt_clr[3], line_width=2, line_alpha = 0.9)

  return p

# Clone the entire repo.
!git clone -l -s https://github.com/fysixs/IsingModel.git /content/Ising
clear_output()
md("<font size=3 color=9467BD> ***Environment Setup Complete! Please run the cell below if you are planning to use Julia for calculations.***")

```

```
#@title
#@markdown  <font color=#8D84B5> ***Setup***
%%shell
set -e

#---------------------------------------------------#
JULIA_VERSION="1.6.2" # any version ≥ 0.7.0
JULIA_PACKAGES="HDF5 Statistics"
JULIA_PACKAGES_IF_GPU="CUDA" # or CuArrays for older Julia versions
JULIA_NUM_THREADS=2
#---------------------------------------------------#

if [ -n "$COLAB_GPU" ] && [ -z `which julia` ]; then
  # Install Julia
  JULIA_VER=`cut -d '.' -f -2 <<< "$JULIA_VERSION"`
  echo "Installing Julia $JULIA_VERSION on the current Colab Runtime..."
  BASE_URL="https://julialang-s3.julialang.org/bin/linux/x64"
  URL="$BASE_URL/$JULIA_VER/julia-$JULIA_VERSION-linux-x86_64.tar.gz"
  wget -nv $URL -O /tmp/julia.tar.gz # -nv means "not verbose"
  tar -x -f /tmp/julia.tar.gz -C /usr/local --strip-components 1
  rm /tmp/julia.tar.gz

  # Install Packages
  if [ "$COLAB_GPU" = "1" ]; then
      JULIA_PACKAGES="$JULIA_PACKAGES $JULIA_PACKAGES_IF_GPU"
  fi
  for PKG in `echo $JULIA_PACKAGES`; do
    echo "Installing Julia package $PKG..."
    julia -e 'using Pkg; pkg"add '$PKG'; precompile;"' &> /dev/null
  done

  # # Launch the julia kernel in a detached tmux session
  # echo "Starting julia kernel..."
  # tmux new -s julia -d
  # tmux send-keys -t julia.0 "julia -i" ENTER

  echo ''
  echo "Success!"
fi
```

###  **1. Defining the basic model**

> **Spins** = $\{\sigma_1, \sigma_2, \ldots, \sigma_n\, |\, \sigma_i = \pm 1\}$

> **External Field** = Off

> **Total Energy** =

$$ \mathcal{H} = -\varepsilon \sum_{i, j \text{ neighbors}} \sigma_i \sigma_j $$

```
#@title <font size = 4 face="Verdana" color=667495> <b>Run Model
#@markdown Please make your selections below. <br> If running anything over $10^6$ iterations `julia` is the clear choice over `python`.
# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
# Data Defs
# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
"""
Data structure for the lattice
"""
class lattice:
  # def __init__(self, nspins, eps, temp, pbc=True):
  def __init__(self, params):
    self.nspins = params['nspins']
    self.eps    = params['eps']
    self.T      = params['temp']
    self.pbc    = params['pbc']
    self.E      = 0.0
    self.M      = 0.0

    probs = np.random.rand( self.nspins )
    self.spins = np.array( [ -1 if prob < 0.5 else 1 for prob in probs ] )

  def energy(self):
    enrg = [ self.spins[i] * self.spins[i+1] for i in range(self.nspins-1) ]
    if (self.pbc): enrg.append( self.spins[0] * self.spins[-1] )
    self.E = -self.eps * sum(enrg)
    return

  def mag(self):
    self.M = self.spins.sum()
    return

# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
# Physics Defs
# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

"""
The Boltzmann factor for energy E in units of 𝜀,
temperature in units of 𝜀/k
"""
def boltz(E, T):
  return np.exp( -E/(k*T) )

def corr(lattice):
  if (lattice.pbc):
    nspins = lattice.nspins
    spins  = lattice.spins

    savg   = spins.mean()
    savg2  = savg*savg
    coeff  = 1 / ( 2 * nspins )

    c = np.ones( (nspins-1)//2 )

    for r in range(1, (nspins-1)//2):
      for i in range(nspins):
        c[r] += spins[i] * ( spins[(i + r)%nspins] + spins[(i - r)%nspins] )

      c[r] *= coeff
      c[r] -= savg2
  return c

# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
# Algorithm Defs
# ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

def MC_flip(lattice, idx):
  nspins = lattice.nspins
  spins  = lattice.spins

  # These are the calculations to obtain ΔE
  # Ef = Ei + eps * ( spins[idx]*spins[idx-1] + spins[idx]*spins[idx+1] ) -
  #           ( (-1)*spins[idx]*spins[idx-1] + (-1)*spins[idx]*spins[idx+1] )
  # Ef = Ei + 2 * ( spins[idx]*spins[idx-1] + spins[idx]*spins[idx+1] )

  if lattice.pbc:
    ΔE = 2 * spins[idx % nspins] * ( spins[(idx-1) % nspins]
                                      + spins[(idx+1) % nspins] )
  else:
    if idx == nspins-1:
      ΔE = 2 * spins[idx] * spins[idx-1]
    elif idx == 0:
      ΔE = 2 * spins[idx] * spins[idx+1]
    else:
      ΔE = 2 * spins[idx] * ( spins[idx-1] + spins[idx+1] )

  ΔM = - 2 * spins[idx]

  return ΔE, ΔM

def MC_step(lattice):
  nspins = lattice.nspins
  idx    = int( np.random.rand() * nspins )

  ΔE, ΔM = MC_flip(lattice, idx)

  if ΔE <= 0.0:
    lattice.spins[idx] *= -1
    lattice.E += ΔE
    lattice.M += ΔM
  else:
    if ( np.random.rand() < boltz(ΔE, lattice.T) ):
      lattice.spins[idx] *= -1
      lattice.E += ΔE
      lattice.M += ΔM
  return

def MC_run(params, data, lattice):
  lattice.energy()
  lattice.mag()

  iter = [params['iters'], params['wrt_it']]

  Eavg = np.zeros(iter[1])
  data['spins'][0,:] = lattice.spins.copy()
  data['energy'][0]  = lattice.E
  data['mag'][0]     = lattice.M

  progPy = widgets.IntProgress(
                    value=0,
                    min=0,
                    max= iter[0] // iter[1] - 1,
                    step=1,
                    description='Calculating:',
                    bar_style='', # 'success', 'info', 'warning', 'danger' or ''
                    orientation='horizontal'
  )
  display(progPy)

  i = 0
  for step in range(iter[0]):
    MC_step(lattice)
    Eavg[i] = lattice.E
    i += 1
    if (step+1)%iter[1] == 0:
      k = step//iter[1]
      progPy.value = k
      data['spins'][k,:] = lattice.spins.copy()
      data['energy'][k]  = Eavg.mean()
      data['mag'][k]     = lattice.M
      i = 0

  data['corr'][:] = corr(lattice)
  return

def run_at_T():

  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
  # Widgets
  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

  # Energies in units of 𝜀,temperatures in units of 𝜀/k.

  items_layout = widgets.Layout( width='auto')

  nspins_input = widgets.BoundedFloatText(value=1e3,
                                          min=10,
                                          max=1e6,
                                          step=1,
                                          description='Spins:',
                                          layout=items_layout)

  eps_input = widgets.BoundedFloatText(value=1.0,
                                       min=0,
                                       max=1e2,
                                       step=0.1,
                                       description='ε:',
                                          layout=items_layout)

  temp_input = widgets.BoundedFloatText(value=1.0,
                                        min=0,
                                        max=20,
                                        step=0.1,
                                        description='Temp:',
                                          layout=items_layout)

  iters_input = widgets.BoundedFloatText(value=1e4,
                                        min=1e3,
                                        max=1e10,
                                        step=1e3,
                                        description='Iterations:',
                                          layout=items_layout)

  runPy_button = widgets.Button(description='ʀᴜɴ ᴏɴ ᴘʏᴛʜᴏɴ',
                                button_style='info', # 'success', 'info', 'warning', 'danger' or ''
                                tooltip='Click me',
                                icon='check',
                                )

  runJu_button = widgets.Button(description='ʀᴜɴ ᴏɴ ᴊᴜʟɪᴀ',
                                button_style='warning', # 'success', 'info', 'warning', 'danger' or ''
                                tooltip='Click me',
                                icon='check'
                                )

  output = widgets.Output()

  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
  # Preparing Data
  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

  def build_params():
    params = {'nspins': int(nspins_input.value),
              'eps'   : float(eps_input.value),
              'temp'  : float(temp_input.value),
              'pbc'   : True,
              'iters' : int(iters_input.value),
              'wrt_it': int(1e3)
              }
    return params

  def build_data(params):
    nspins = params['nspins']
    iters  = params['iters']
    wrt_it = params['wrt_it']

    data   = h5py.File("./Ising1D.hdf5", "w")
    data.create_dataset('params',(5, ), dtype='f')
    data['params'][0] = params['nspins']
    data['params'][1] = params['eps']
    data['params'][2] = params['temp']
    data['params'][3] = params['iters']
    data['params'][4] = params['wrt_it']

    data.create_dataset('energy',
                      (iters // wrt_it, ), dtype='f')
    data.create_dataset('mag',
                      (iters // wrt_it, ), dtype='f')
    data.create_dataset('corr',
                      ((nspins-1)//2, ), dtype='f')
    data.create_dataset('spins',
                      (iters // wrt_it, nspins), dtype='f')
    return data

  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
  # Interface
  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

  def runPy_button_clicked(b):
    with output:
      output.clear_output()
      params = build_params()
      data   = build_data(params)

      lattice_pbc = lattice(params)
      MC_run(params, data, lattice_pbc)

      display(md("<font size = 3 color=9467BD><b>Run complete!"))

      data.close()
    return

  runPy_button.on_click(runPy_button_clicked)

  def run(params):
    julia_call = ['julia', '/content/Ising/MCRun1D.jl', str(params['nspins']),
                    str(params['eps']), str(params['temp']), str(params['iters']),
                    str(params['wrt_it'])]

    prog = widgets.IntProgress(
                    value=0,
                    min=0,
                    max= params['iters'] // params['wrt_it'] - 1,
                    step=1,
                    description='Calculating:',
                    bar_style='', # 'success', 'info', 'warning', 'danger' or ''
                    orientation='horizontal'
    )
    display(prog)
    with subprocess.Popen(julia_call, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as process:
      for line in process.stdout:
        prog.value += 1

  def runJu_button_clicked(b):
    with output:
      output.clear_output()
      params = build_params()

      run(params)

      display(md("<font size = 3 color=9467BD><b>Run complete!"))
    return

  runJu_button.on_click(runJu_button_clicked)

  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ
  # UI
  # ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ

  UI = GridBox(children=[nspins_input, eps_input, runPy_button,
                         temp_input, iters_input, runJu_button],
        layout=widgets.Layout(
            width='70%',
            grid_template_columns='200px 200px 100px',
            grid_template_rows='30px 30px',
            grid_gap='10px 10px')
       )
  # buttons = widgets.HBox([runPy_button, runJu_button])
  display( UI )
  display(output)
  return

run_at_T()
```

```
#@title <font size = 4 face="Verdana" color=667495> <b>Visualize Output
#@markdown Please run this cell to visualize the calculations.
data = h5py.File("./Ising1D.hdf5", "r")

steps  = len(data['energy'])

conf_i = plot_spins( data, 0)
conf_f = plot_spins( data, steps - 1)
enrg   = plot_energy( data )
cor    = plot_corr(data)
spinplots = row( conf_i, conf_f )

p1   = column( spinplots, enrg )
tab1 = Panel(child=p1, title="Spin Configutation & Energy")

p2   = column( spinplots, plot_mag( data ) )
tab2 = Panel(child=p2, title="Magnetization")

p3   = column(spinplots, cor)
tab3 = Panel(child=p3, title="Correlation")

# spinplots = row( conf_i, conf_f )
# plots     = column( spinplots, enrg, mag[0], mag[1], cor )
# show(plots)
show(Tabs(tabs=[tab1, tab2, tab3]))

display(md(
    f"<font size = 3 color=9467BD><b> Initial Energy = {data['energy'][0]}ε ⟶ Final Energy = {data['energy'][-1]}ε"
     ))

data.close()
```

#  *References*

1. [An Introduction to Thermal Physics](https://www.amazon.com/Introduction-Thermal-Physics-Daniel-Schroeder/dp/0192895540) - Daniel V. Schroeder