# Getting Started With Python

## Installation

There are various ways to install Python. Assuming the reader is not (yet) well versed in programming, I suggest to download the Anaconda distribution, which works for all major operating systems (Mac OS X, Windows, Linux) and provides a fully fledged Python installation including necessary libraries and Jupyter notebooks.

* Go to https://www.anaconda.com/distribution/#download-section
* Download latest version corresponding to your OS
* Run .exe/.pkg file.
  - Make sure to set flag "Add Anaconda to my PATH environment variable" and
  - "Register Anaconda as my default Python 3.x"

Be sure to download the latest Python 3.x version (not 2.x; backward compability is not always given!).

The installation should not cause any problems. If you wisch a step-by-step guide (incl. some further insights) see [Cyrille Rossants excellent notebook](http://nbviewer.jupyter.org/github/ipython-books/minibook-2nd-code/blob/master/chapter1/12-installation.ipynb) or simply the [Jupyter Documentation](https://jupyter.org/documentation) on the topic.

## IPyton / Jupyter Notebooks

What you see here is a so called Jupyter notebook. It makes it possible to interactively combine code with output (results, graphics), markdown text and LaTeX (for mathematical expressions). All codes discussed in this course will be provided through such notebooks and you soon will understand and appreciate the functionality they provide.

* The basic markdown commands are well summarized [here](http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Working%20With%20Markdown%20Cells.html).
* LaTeX is a typesetting language with extensive capabilities to typeset math. For a basic introductin to math in LaTeX see sections 3.3 - 3.4 (p. 22 - 33) of *More Math into LaTeX* by Grätzer (2007), [available as pdf here](http://static.latexstudio.net/wp-content/uploads/2014/09/math.into_.Latex_.4ed.pdf)

If you are keen on learning more about IPython/Jupyter, consider [this notebook](http://nbviewer.jupyter.org/github/ipython-books/minibook-2nd-code/blob/master/chapter1/13-nbui.ipynb) - a well written introduction by Cyrille Rossant - or watch this video.

```python
from IPython.display import YouTubeVideo
from datetime import timedelta

YouTubeVideo('jZ952vChhuI')
```

## Data Types in Python

### Building Blocks

To analyze data in Python, data has to be stored as some kind of data type. These data types form the structure of the data and make data easily accessible. Python's basic building blocks are:
* Numbers (integer, floating point, and complex)
* Booleans (true/false)
* Strings
* Lists
* Dictionaries
* Tuples

We will discuss the first four data types above as these are relevant for us.

Python is a dynamically typed language, meaning that - unlike in static languages such as VBA, C, Java etc. - you do not explicitly need to assign a data type to a variable. Python will do that for you. A few examples will explain this best:

```python
a = 42       # In VBA you would first have to define data type, only then the value: Dim a as integer; a = 42
b = 10.3     # VBA: Dim b as Double; b = 10.3
c = 'hello'  # VBA: Dim c as String; c = "hello"
d = True     # VBA: Dim d as Boolean; d = True

print('a: ', (a))
print('b: ', type(b))
print('c: ', type(c))
print('d: ', type(d))
```

    a:  42
    b:  <class 'float'>
    c:  <class 'str'>
    d:  <class 'bool'>

### Running Code in Jupyter

How did I execute this code section? If I only want to run a code cell, I select the cell and hit `ctrl + enter`. If you wish to run the entire notebook, go to Kernel (dropdown menu) and select "Restart & Run All". There are lots of shortcuts that let you handle a Jupyter notebook just from the keyboard. Press `H` on your keyboard (or go to Help/Keyboard Shortcuts) to see all of the shortcuts.

### Simple Arithmetics
Simple arithmetic operations are straight forward:

```python
a = 2 + 4 - 8      # Addition & Subtraction
b = 6 * 7 / 3 - 2  # Multiplication & Division
c = 2**(1/2)       # Exponents & Square root
d = 10 % 3         # Modulus
e = 10 // 3        # Floor division

print(' a =', a, '\n',
      'b =', b, '\n',
      'c =', c, '\n',
      'd =', d, '\n',
      'e =', e)
```

     a = -2
     b = 12.0
     c = 1.4142135623730951
     d = 1
     e = 3

We can even use arithmetic operators to concatenate strings:

```python
a = 'Hello'
b = 'World!'
print(a + ' ' + b)

print(a * 3)
```

    Hello World!
    HelloHelloHello

### Lists
Now let's look at lists. Lists are capable of combining multiple data types.

```python
e = ['Calynn', 'Dillon', '10.3', c, d]
print('e: ', type(e))
print([type(item) for item in e])
```

    e:  <class 'list'>
    [<class 'str'>, <class 'str'>, <class 'str'>, <class 'float'>, <class 'int'>]

Note that the third element in list `e` is set in quotation marks and thus Python interprets this as string.

## NumPy Arrays

### NumPy Arrays from Lists

For as useful list appear, its flexibility come at a high cost. Because each element contains not only the value itself but also information about the data type, storing data in list consumes a lot of memory. For this reason the Python community introduced NumPy (short for Numerical Python). Among other things, this package provides **fixed-type arrays** which are more efficient to store and operate on dense data than simple lists.

Fixed-type arrays are dense arrays of uniform type. Uniform here means that all entries of the array have the same data type, e.g. all are floating point numbers. We start by importing the NumPy package (following general convention we import this package under the alias name `np`) and create some simple NumPy arrays form Python lists.

```python
import numpy as np

# Integer array
np.array([3, 18, 12])
```

    array([ 3, 18, 12])

```python
# Floating point array
np.array([3., 18, 12])
```

    array([ 3., 18., 12.])

Similarly, we can explicitly set the data type:

```python
np.array([3, 18, 12], dtype='float32')
```

    array([ 3., 18., 12.], dtype=float32)

```python
# Multidimensional arrays
np.array([range(i, i + 3) for i in [1, 2, 3]])
```

    array([[1, 2, 3],
           [2, 3, 4],
           [3, 4, 5]])

We've seen above that we can define the data type for NumPy arrays. A list of available data types can be found in the [NumPy documentation](https://docs.scipy.org/doc/numpy/user/basics.types.html).

### NumPy Arrays from Scratch
Sometimes it is helpful to create arrays from scratch. Here are some examples:

```python
# Integer array with 8 zeros
np.zeros(shape=8, dtype='int')
```

    array([0, 0, 0, 0, 0, 0, 0, 0])

```python
# 2x3 floating-point array filled with 1s
np.ones((2, 3), 'float32')
```

    array([[1., 1., 1.],
           [1., 1., 1.]], dtype=float32)

Note that I do not need to use `np.ones(shape=(2, 3), dtype='float32')` to define the shape. It's ok to go with the short version as long as the order is correct, Python will understand. However, going with the explicit version helps to make your code more readable and I encourage students to follow this advice.

Each predefined function is documented in a help page. One can search for it by calling `?np.ones`, `np.ones?` or `help(np.ones)`. If it is not clear how the function is precisely called, it is best to use * (wildcard character) - as in `np.*ne*?`. This will list all functions in the NumPy library which contain 'ne' in their name. In our example `np.ones?` shows the following details:

```python
# np.ones?
```

The function description also shows examples of how the function can be used. Often this is very helpful, but for the sake of brevity, these are omitted here.

In the function description we see that the order of arguments is `shape, dtype, order`. If our inputs are in this order, one does not need to specify the argument. Furthermore, we see that `dtype` and `order` is optional, meaning that if left undefined, Python will simply use the default argument. This makes for a very short code. However, going with the longer explicit version helps to make your code more readable and I encourage students to follow this advice.

```python
# 3x2 array filled with 2.71
np.full(shape=(3, 2), fill_value=2.71)
```

    array([[2.71, 2.71],
           [2.71, 2.71],
           [2.71, 2.71]])

```python
# 3x3 boolean array filled with 'True'
np.full((2, 2), 1, bool)
```

    array([[ True,  True],
           [ True,  True]])

```python
# Array filled with linear sequence
np.arange(start = 0, stop = 1, step = 0.1)  # or simply np.arrange(0, 1, 0.1)
```

    array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

```python
# Array of evenly spaced values
np.linspace(start = 0, stop = 1, num = 4)
```

    array([0.        , 0.33333333, 0.66666667, 1.        ])

Arrays with random variables are easily created. Below three examples. See the [numpy.random documentation page](https://docs.scipy.org/doc/numpy/reference/routines.random.html) for details on how to generate other random variable-arrays.

```python
# 4x4 array of uniformly distributed random variables
np.random.random((4, 4))
```

    array([[0.95282824, 0.65194764, 0.18336745, 0.79707386],
           [0.90011522, 0.09318048, 0.23252546, 0.27295939],
           [0.05777291, 0.22659345, 0.25981756, 0.03744564],
           [0.2129678 , 0.46855791, 0.40119633, 0.53802224]])

```python
# 3x3 array of normally distributed random variables (with mean = 4, sd = 6)
np.random.normal(loc = 4, scale = 6, size = (3, 3))
```

    array([[ 9.267651  ,  7.39315744,  5.39772066],
           [ 4.83828668, -2.72195724,  5.76856645],
           [-3.51746614,  0.29227648, -4.76363532]])

```python
# 3x3 array of random integers in interval [0, 15)
np.random.randint(low = 0, high = 15, size = (3, 3))
```

    array([[12,  3, 10],
           [ 9,  7, 14],
           [13,  3,  6]])

```python
# 4x4 identity matrix
np.eye(4)
```

    array([[1., 0., 0., 0.],
           [0., 1., 0., 0.],
           [0., 0., 1., 0.],
           [0., 0., 0., 1.]])

### NumPy Array Attributes
Each NumPy array has certain attributes.

Here are some attributes we can call:

| Attribute | Description            |
|-----------|------------------------|
| `ndim`    | No. of dimensions      |
| `shape`   | Size of each dimension |
| `size`    | Total size of array    |
| `dtype`   | Data type of array     |
| `itemsize` | Size (in bytes)       |
| `nbytes`   | Total size (in bytes) |

To show how one can access them we'll define three arrays.

```python
np.random.seed(1234)  # Set seed for reproducibility

x = np.random.randint(10, size = 6)          # 1-dimensional array (vector)
y = np.random.randint(10, size = (3, 4))     # 2-dimensional array (matrix)
z = np.random.randint(10, size = (3, 4, 5))  # 3-dimensional array
```

And here's how we call for these properties:

```python
print(' ', x, '\n\n', y, '\n\n', z)
```

      [3 6 5 4 8 9]

     [[1 7 9 6]
     [8 0 5 0]
     [9 6 2 0]]

     [[[5 2 6 3 7]
      [0 9 0 3 2]
      [3 1 3 1 3]
      [7 1 7 4 0]]

     [[5 1 5 9 9]
      [4 0 9 8 8]
      [6 8 6 3 1]
      [2 5 2 5 6]]

     [[7 4 3 5 6]
      [4 6 2 4 2]
      [7 9 7 7 2]
      [9 7 4 9 0]]]

```python
print('ndim:      ', z.ndim)
print('shape:     ', z.shape)
print('size:      ', z.size)
print('data type: ', z.dtype)
print('itemsize:  ', z.itemsize)
print('nbytes:    ', z.nbytes)
```

    ndim:       3
    shape:      (3, 4, 5)
    size:       60
    data type:  int32
    itemsize:   4
    nbytes:     240

### Index: How to Access Elements
What might be a bit counterintuitive at the beginning is that **Python's indexing starts at 0**. Other than that, accessing the $i$'th element (starting at 0) of a list or a array is straight forward.

```python
print(e, '\n')  # List from above
print(x, '\n')  # One dimensional np array from above
print(y, '\n')  # Two dimensional np array from above
```

    ['Calynn', 'Dillon', '10.3', 1.4142135623730951, 1]

    [3 6 5 4 8 9]

    [[1 7 9 6]
     [8 0 5 0]
     [9 6 2 0]]

```python
e[2]
```

    '10.3'

```python
e[2] * 2
```

    '10.310.3'

```python
x[5]
```

    9

```python
y[2, 0]  # Note again that [m, n] starts counting for both rows (m) as well as columns (n) from 0
```

    9

To access the end of an array, you can also use negative indices:

```python
e[-1]
```

    1

```python
y[-2, 2]
```

    5

Arrays are also possible as inputs:

```python
ind = [3, 5, -4]
x[ind]
```

    array([4, 9, 5])

```python
x = np.arange(12).reshape((3, 4))
print(x)
row = np.array([1, 2])
col = np.array([0, 3])
x[row, col]
```

    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]

    array([ 4, 11])

Knowing the index we can also replace elements of an array:

```python
x[0] = 99
x
```

    array([[99, 99, 99, 99],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])

**IMPORTANT NOTE: **

**NumPy arrays have a fixed type. This means that e.g. if you insert a floating-point value to an integer array, the value will be truncated!**

```python
x[0] = 3.14159; x
```

    array([[ 3,  3,  3,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])

### Array Slicing
We can also use square brackets to access a subset of the data. The syntax is:

`x[start:stop:step]`

The default values are: `start=0`, `stop='size of dimension'`, `step=1`

```python
x = np.arange(10)
x
```

    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

```python
x[:3]  # First three elements
```

    array([0, 1, 2])

```python
x[7:]  # Elements AFTER 7th element
```

    array([7, 8, 9])

```python
x[4:8]  # Element 5, 6, 7 and 8
```

    array([4, 5, 6, 7])

```python
x[::2]  # Even elements
```

    array([0, 2, 4, 6, 8])

```python
x[1::2]  # Odd elements
```

    array([1, 3, 5, 7, 9])

```python
x[::-1]  # All elements reversed
```

    array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

```python
x[::-2]  # Odd elements reversed
```

    array([9, 7, 5, 3, 1])

Array slicing works the same for multidimensional arrays.

```python
y  # from above
```

    array([[1, 7, 9, 6],
           [8, 0, 5, 0],
           [9, 6, 2, 0]])

```python
y[:2, :3]  # Rows 0 and 1, columns 0, 1, 2
```

    array([[1, 7, 9],
           [8, 0, 5]])

```python
y[:, 2]  # Third column
```

    array([9, 5, 2])

```python
y[0, :]  # First row
```

    array([1, 7, 9, 6])

**IMPORTANT NOTE:**

**When slicing and assigning part of an existing array to a new variable, the new variable will only hold a "view" but not a copy. This means, that if you change a value in the new array, the original array will also be changed. The idea behind this is to save memory. But fear not: with the ".copy()" method, you still can get a true copy.**

Here a few corresponding examples for better understanding:

```python
ySub = y[:2, :2]
print(ySub)
```

    [[1 7]
     [8 0]]

```python
ySub[0, 0] = 99
print(ySub, '\n')
print(y)
```

    [[99  7]
     [ 8  0]]

    [[99  7  9  6]
     [ 8  0  5  0]
     [ 9  6  2  0]]

```python
ySubCopy = y[:2, :2].copy()
ySubCopy[0, 0] = 33
print(ySubCopy, '\n')
print(y)
```

    [[33  7]
     [ 8  0]]

    [[99  7  9  6]
     [ 8  0  5  0]
     [ 9  6  2  0]]

### Concatenating, Stacking and Splitting
Often it is useful to combine multiple arrays into one or to split a single array into multiple arrays. To accomplish this, we can use NumPy's `concatenate` and `vstack`/`hstack` function.

```python
x = np.array([1, 2, 3])
y = np.array([11, 12, 13])
z = np.array([21, 22, 23])
np.concatenate([x, y, z])
```

    array([ 1,  2,  3, 11, 12, 13, 21, 22, 23])

```python
# Stack two vectors horizontally
np.hstack([x, y])
```

    array([ 1,  2,  3, 11, 12, 13])

```python
# Stack two vectors vertically
np.vstack([x, y])
```

    array([[ 1,  2,  3],
           [11, 12, 13]])

```python
# Stack matrix with column vector
m = np.arange(0, 9, 1).reshape((3, 3))
np.vstack([m, z])
```

    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 6,  7,  8],
           [21, 22, 23]])

```python
# Stack matrix with row vector
np.hstack([m, z.reshape(3, 1)])
```

    array([[ 0,  1,  2, 21],
           [ 3,  4,  5, 22],
           [ 6,  7,  8, 23]])

The opposite of concatenating is splitting. Numpy has `np.split`, `np.hsplit` and `np.vsplit` functions. Each of these takes a list of indices, giving the split points, as input.

```python
x = np.arange(8.0)
a, b, c = np.split(x, [3, 5])
print(a, b, c)
```

    [0. 1. 2.] [3. 4.] [5. 6. 7.]

```python
x = np.arange(16).reshape(4, 4)
upper, lower = np.vsplit(x, [3])
print(upper, '\n\n', lower)
```

    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]

     [[12 13 14 15]]

```python
left, right = np.hsplit(x, [2])
print(left, '\n\n', right)
```

    [[ 0  1]
     [ 4  5]
     [ 8  9]
     [12 13]]

     [[ 2  3]
     [ 6  7]
     [10 11]
     [14 15]]

## Conditions

### Boolean Operators

Boolean operators check an input and return either `True` (equals 1 as value) or `False` (equals 0). This is often very helpful if one wants to check for conditions or sort out part of a data set which meet a certain condition. Here are the common comparison operators:

| **Operator** | **Description**            |
|:------------:|----------------------------|
| ==           | equal ($=$)                |
| !=           | not equal ($\neq$)         |
| <            | less than ($<$)            |
| <=           | less or equal ($\leq$)     |
| >            | greater ($>$)              |
| >=           | greater or equal ($\geq$)  |
| &            | Mathematical AND ($\land$) |
| |       | Mathematical OR ($\lor$)   |
| `in`         | element of ($\in$)         |

The following sections give a glimpse of how these operators can be used.

```python
x = np.arange(start=0, stop=8, step=1)
print(x)
print(x == 2)
print(x != 3)
print((x < 2) | (x > 6))
```

    [0 1 2 3 4 5 6 7]
    [False False  True False False False False False]
    [ True  True  True False  True  True  True  True]
    [ True  True False False False False False  True]

```python
# Notice the difference
print(x[x <= 4])
print(x <= 4)
```

    [0 1 2 3 4]
    [ True  True  True  True  True False False False]

#### If ... else statements

These statements check a given condition and depending on the result (`True`, `False`) execute a subsequent code. As usual, an example will do. Notice that indentation is necessary for Python to correctly compile the code.

```python
x = 3

if x%2 == 0:
    print(x, 'is an even number')
else:
    print(x, 'is an odd number')

```

    3 is an odd number

It is also possible to have more than one condition as the next example shows.

```python
x = 20

if x > 0:
    print(x, 'is positive')
elif x < 0:
    print(x, 'is negative')
else:
    print(x, 'is neither strictly positive nor strictly negative')
```

    20 is positive

Combining these two statements would make for a nested if ... else statement.

```python
x = -3
if x > 0:
    if (x%2) == 0:
        print(x, 'is positive and even')
    else:
        print(x, 'is positive and odd')
elif x < 0:
    if (x%2) == 0:
        print(x, 'is negative and even')
    else:
        print(x, 'is negative and odd')
else:
    print(x, 'is 0')
```

    -3 is negative and odd

### Loops

#### "For" Loops

"For" loops iterate over a given sequence. They are very easy to implement as the following example shows. We start with an example and give some explanations afterwards.

For our example, let's assume you ought to sum up the integer values of a sequence from 10 to 1 with a loop. There are obviously more efficient ways of doing this but this serves well as an introductory example. From primary school we know the result is easily calculated as

$$
\begin{equation}
    \sum_{i=1}^n x_i = \dfrac{n (n+1)}{2} \qquad -> \qquad \dfrac{10 \cdot 11}{2} = 55
\end{equation}
$$

```python
seq = np.arange(start=10, stop=0, step=-1)
seqSum = 0
for value in seq:
    seqSum = seqSum + value

seqSum
```

    55

A few imprtant notes:
* Indentation is not just here for better readability of the code but it is actually necessary for Python to correctly interpret the code.
* Though it is not necessary, we initiate `seqSum = 0` here. Otherwise, if we run the code repeatedly we add to the previous total!
* `value` takes on every value in array `seq`. In the first loop `value=10`, second loop `value=9`, etc.

Loops can be nested, too. Here's an example.

```python
seq = seq.reshape(2, 5)
seqSum = 0
row, col = seq.shape

for rowIndex in range(0, row):
    for colIndex in range(0, col):
        seqSum = seqSum + seq[rowIndex, colIndex]

seqSum
```

    55

#### "While" Loops

"While" loops execute as long as a certain boolean condition is met. Picking up the above example we can formulate the following loop:

```python
seqSum = 0
i = 10
while i >= 1:
    seqSum = seqSum + i
    i = i - 1  # Also: i -= 1

print(seqSum)
```

    55

### Functions

Functions come into play when either a task needs to be performed more than once or when it helps to reduce the complexity of a code.

Following up on our play examples from above, let us assume we are tasked to write a function which sums up all even and all odd integers of a vector.

```python
def sumOddEven(vector):
    """Calculates sum of odd and even numbers in array.

    Args:
        vector: NumPy array of length n

    Returns:
        odd: Sum of odd numbers
        even: Sum of even numbers
    """

    # Initiate values
    odd = 0
    even = 0

    # Loop through values of array; check for each
    # value whether it is odd or even and add to
    # previous total.
    for value in vector:
        if (value % 2) == 0:
            even = even + value
        else:
            odd = odd + value

    return odd, even

# Initiate array [1, 2, ..., 99, 100]
seq = np.arange(1, 101, 1)

# Apply function and print results
odd, even = sumOddEven(seq)
print('Odd: ', odd, ', ', 'Even: ', even)
```

    Odd:  2500 ,  Even:  2550

## Commenting

Above code snippet not only shows how functions are set up but also displays the importance of comments. Comments are preceeded by a hash sign (#), such that the interpreter will not parse what follows the hash.  When programming, you should always comment your code to notate your work. This details your steps/thoughts/ideas not only for other developers but also for you when you pick up your code some time after writing it. Good programmers make heavy use of commenting and I strongly encourage the reader to follow this standard.

## Slowness of Loops

It is at this point important to note that loops should only be used as a last resort. Below we show why. The first code runs our previously defined function. The second code uses NumPy's built-in function.

```python
%%timeit
seq = np.arange(1,10001, 1)
sumOddEven(seq)
```

    7.66 ms ± 728 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

```python
%%timeit
seq[(seq % 2) == 0].sum()
seq[(seq % 2) == 1].sum()
```

    13.8 µs ± 356 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Above timing results show what was hinted before: In 9'999 out of a 10'000 cases it is significantly faster using already built in functions compared to loops. The simple reason is that modules such as NumPy or Pandas use (at their core) optimized compile code to calculate the results and this is most certainly faster than a loop.

So in summary: Above examples helped introduce if statements, loops and functions. In real life, however, you should check if Python does not already offer a built-in function for your task. If yes, make sure to use it.

## Broadcasting

### Computations on Arrays

In closing this chapter we briefly introduce NumPy's broadcasting functionality. Rules for matrix arithmetic apply to NumPy arrays as one would expect and it is left to the reader to explore it. Broadcasting, however, goes one step further in that it allows for element-by-element operations on arrays (and matrices) of different dimensions - which under normal rules would not be compatible. An example shows this best.

```python
M = np.ones(shape=(3, 3))
v = np.array([1, 2, 3])
M + v
```

    array([[2., 3., 4.],
           [2., 3., 4.],
           [2., 3., 4.]])

```python
# Notice the difference
vecAdd = v + v
broadAdd = v.reshape((3, 1)) + v

print(vecAdd, '\n')
print(broadAdd)
```

    [2 4 6]

    [[2 3 4]
     [3 4 5]
     [4 5 6]]

## Further Resources

The following ressources, which were consulted to write this notebook, are recommended to better acquaint yourself with Python and NumPy:

* Vanderplas, Jake, 2016, *Python Data Science Handbook* (O'Reilly Media, Sebastopol, CA).
* Sheppard, Kevin, 2017, Introduction to Python for Econometrics, Statistics and Data Analysis from Website https://www.kevinsheppard.com/images/b/b3/Python_introduction-2016.pdf, 07/07/2017.
* Paarsch, Harry J., and Golyaev, Konstantin, 2016, *A Gentle Introduction to Effective Computing in Quantitative Research: What Every Research Assistant Should Know*, MIT Press, Cambridge, MA.


```python
from IPython.display import HTML

HTML('''
<form action="javascript:code_toggle()"><input type="submit" value="Code Toggle"></form>''')
```

<form action="javascript:code_toggle()"><input type="submit" value="Code Toggle"></form>

```python
from IPython.display import HTML

HTML('''
<a href="https://raw.githubusercontent.com/{{ site.links.repo }}/master/benchmarks/benchmark4.ipynb"
   download="benchmark4.ipynb">
<button type="submit">Download Notebook</button>
</a>''')
```

<button type="submit">Download Notebook</button>

# Benchmark Problem 4: Elastic Precipitate

```python
from IPython.display import HTML

HTML('''{% include jupyter_benchmark_table.html num="[4]" revision=1 %}''')
```

{% include jupyter_benchmark_table.html num="[4]" revision=1 %}

See the journal publication entitled ["Phase Field Benchmark Problems for Dendritic Growth and Linear Elasticity"][paper] for more details about the benchmark problems. Furthermore, read [the extended essay][benchmarks] for a discussion about the need for benchmark problems.

[benchmarks]: ../
[paper]: https://doi.org/10.1016/j.commatsci.2018.03.015

## Overview

Precipitates are a key microstructural feature impacting the strength of alloys [[1][chawla1999mechanical]], and they are often elastically stressed, which affects their shape and their microstructure evolution during service.  Elasticity has long been incorporated into phase field models: indeed, Cahn's seminal paper on spinodal decomposition [[2][cahn1961spinodal]] incorporates elastic strains due to composition fluctuations.  Eshelby presents an analytical solution for the elastic field of a single coherent, elastically stressed precipitate in an infinite matrix [[3][eshelby1957determination]], but the generalized problem of multiple interacting precipitates in a matrix with arbitrary crystal structure, lattice parameter misfit and elastic stiffnesses can only be solved numerically.  Sharp-interface approaches provide insight into equilibrium elastic shapes and coarsening under the influence of elastic stress [[4][voorhees1992morphological], [5][thompson1994equilibrium], [6][su1996dynamicsI], [7][su1996dynamicsII], [8][akaiwa2001large]], but these approaches have difficulty simulating precipitate splitting or merging. Early phase field formulations studying elastically stressed precipitates demonstrate the power of the method (e.g., Refs. [[9][wang1993shape], [10][wang1994effect], [11][wang1995shape]]), and present-day studies have expanded to 3D simulations (e.g., Refs. [[12][goerler2017topological], [13][radhakrishnan2016phase], [14][shi2015microstructure], [15][cottura2015role]]) and formulations that include plasticity (e.g., Refs. [[16][cottura2016coupling], [17][ammar2014modelling], [18][guo2008elastoplastic]]).

[chawla1999mechanical]: https://dx.doi.org/10.1016/S1369-7021(09)70086-0
[cahn1961spinodal]: https://dx.doi.org/10.1002/9781118788295.ch11
[eshelby1957determination]: https://dx.doi.org/10.1098/rspa.1957.0133
[voorhees1992morphological]: https://dx.doi.org/10.1016/0956-7151(92)90462-N
[thompson1994equilibrium]: https://dx.doi.org/10.1016/0956-7151(94)90036-1
[su1996dynamicsI]: https://dx.doi.org/10.1016/1359-6454(95)00284-7
[su1996dynamicsII]: https://dx.doi.org/10.1016/1359-6454(95)00285-5
[akaiwa2001large]: https://dx.doi.org/10.1006/jcph.2001.6842
[wang1994effect]: https://dx.doi.org/10.1016/0956-716X(94)90130-9
[wang1995shape]: https://dx.doi.org/10.1111/j.1151-2916.1993.tb06605.x
[goerler2017topological]: https://dx.doi.org/10.1016/j.actamat.2016.10.059
[radhakrishnan2016phase]: https://dx.doi.org/10.1007/s11661-016-3746-6
[shi2015microstructure]: https://dx.doi.org/10.1016/j.actamat.2015.04.050
[cottura2015role]: https://dx.doi.org/10.1016/j.actamat.2015.04.034
[cottura2016coupling]: https://dx.doi.org/10.1016/j.jmps.2016.05.016
[ammar2014modelling]: https://dx.doi.org/10.1007/s11012-014-0011-1
[guo2008elastoplastic]: https://dx.doi.org/10.1016/j.jnucmat.2008.05.008

## Model Formulation

In this formulation, one phenomenological order parameter, $\eta$, is evolved, which has a value of 0 in the matrix and a value of 1 in the precipitate for an unstressed system with planar interfaces. This choice makes interpolation of materials properties between phases straightforward. The free energy of the system, $\mathcal{F}$, includes contributions from interfacial and elastic energy and is expressed as

\begin{equation}
\mathcal{F}=\int_{V}\left(f_{\text{bulk}}\left(\eta\right) + \frac{\kappa}{2}|\nabla \eta|^{2} + f_{\text{el}}\left(\eta\right) \right)dV,
\end{equation}

where $f_{\text{bulk}}$ is the bulk free energy density, $\kappa$ is the gradient energy coefficient, and $f_{\text{el}}$ is the local elastic free energy density. The $f_{\text{bulk}}$ term is a symmetric double-well with minima of zero, such that its contribution is only to the interfacial energy. As discussed in Ref. [[1][jokisaari2017superalloy]], we choose $f_{\text{bulk}}$ to have a 10th-order polynomial form,

\begin{equation}
f_{\text{bulk}}=w\sum_{j=0}^{10}a_j\eta^j,
\end{equation}

which makes the energy wells of the matrix and precipitate phases deep and narrow.  This prevents the actual value of $\eta$ in each phase from shifting significantly from its equilibrium value due to the presence of a curved interface or elastic strain.  The height of the energy barrier is controlled by $w$. The $f_{\text{bulk}}$ coefficients are given in the following table, and ensure that $f_{\text{bulk}}\left(0\right)=f_{\text{bulk}}\left(1\right)=f_{\text{bulk}}'\left(0\right)=f_{\text{bulk}}'\left(1\right)=0$ and that the energy curve remains concave down between the two energy wells.

| Parameter | Value |
|:-----:|-----------------|
| $a_0$ | 0               |
| $a_1$ | 0               |
| $a_2$ | 8.072789087     |
| $a_3$ | -81.24549382    |
| $a_4$ | 408.0297321     |
| $a_5$ | -1244.129167    |
| $a_6$ | 2444.046270     |
| $a_7$ |  -3120.635139   |
| $a_8$ |  2506.663551    |
| $a_9$ | -1151.003178    |
| $a_{10}$ | 230.2006355   |

The large number of significant digits are necessary to ensure that the first derivative of $f_{\text{bulk}}$ is zero at $\eta=0$ and $\eta=1$.

The elastic energy density is given as [[1][jokisaari2017superalloy]]

\begin{equation}
f_{\text{el}}\left(\eta\right)=\frac{1}{2}\sigma_{ij} \epsilon_{ij}^{\text{el}},
\end{equation}

where $\sigma_{ij}=C_{ijkl}\left(\eta\right) \epsilon_{ij}^{\text{el}}$ is the elastic stress, $\epsilon_{ij}^{\text{el}}$ is the elastic strain, and $C_{ijkl}\left(\eta\right)$ is the elastic stiffness tensor such that the system is mechanically stable (the Einstein summation convention is used).  To incorporate the dependence of the elastic stiffness on the phase, the stiffness is interpolated smoothly from one phase to the other across the diffuse interface,

\begin{equation}
C_{ijkl}\left(\eta\right)= C_{ijkl}^{\text{matrix}}\left[1-h\left(\eta\right)\right]+C_{ijkl}^{\text{precip}} \, h\left(\eta\right),
\end{equation}
where $C_{ijkl}^{\text{matrix}}$ and $C_{ijkl}^{\text{precip}}$ are the stiffness tensors of the matrix and precipitate phases, respectively, and $h\left(\eta\right)=\eta^3\left(6\eta^2-15\eta+10\right)$ is a smooth interpolation function that ensures that $h\left( 0 \right ) = h'\left( 0 \right)=h'\left( 1 \right)=0$ and $h\left(1\right) = 1$ [[2][leo1998diffuse]].

Because the lattice parameters of the two phases are different, the elastic strain differs from the total strain, $\epsilon_{ij}^{\text{total}}$, as [[3][eshelby1957determination]]

\begin{equation}
\epsilon_{ij}^{\text{el}}=\epsilon_{ij}^{\text{total}}-\epsilon_{ij}^{0}\left(\eta\right),
\end{equation}

where $\epsilon_{ij}^{0}$ is the local stress-free strain. It is calculated as

\begin{equation}
\epsilon_{ij}^0\left(\eta\right)= \epsilon_{ij}^T \, h\left(\eta\right),
\end{equation}

where $\epsilon_{ij}^{T}$ is the crystallographic misfit strain tensor between the matrix and precipitate phases defined with respect to the matrix.  Finally, the total strain is related to the displacements, $u_i$, as [[3][eshelby1957determination]]

\begin{equation}
\epsilon_{ij}^{\text{total}}=\frac{1}{2}\left[\frac{\partial u_i}{\partial x_j}+\frac{\partial u_j}{\partial x_i}\right].
\end{equation}

In this problem, precipitate shapes must evolve to their equilibrium shape while remaining as small particles embedded in a much larger matrix. To do so, we employ the Cahn-Hilliard equation to perform fictive time evolution [[2][leo1998diffuse], [1][jokisaari2017superalloy]], which conserves the total integral of $\eta$ within the simulation. The evolution of $\eta$ is given as

\begin{equation}
\frac{\partial\eta}{\partial t}=\nabla\cdot\left[M\nabla\left\{ \frac{\delta \mathcal{F}}{\delta\eta}\right\} \right],
\end{equation}

where $M$ is the mobility and the chemical potential is

\begin{equation}
\mu \equiv \frac{\delta \mathcal{F}}{\delta\eta}=\frac{\partial f_{\text{chem}}}{\partial\eta}+\frac{\partial f_{\text{elastic}}}{\partial\eta}-\kappa\nabla^{2}\eta.
\end{equation}

We have flexibility in choosing $M$, as we are only interested in the final state of the system. Furthermore, we assume that the relaxation dynamics for elasticity are much faster than for the diffusion of $\eta$, as is generally the case for phase field models.  As such, we solve the time-independent equation for mechanical equilibrium at each time step,

\begin{equation}
\nabla\cdot\sigma_{ij} = 0.
\end{equation}

[jokisaari2017superalloy]: https://arxiv.org/abs/1709.02010
[leo1998diffuse]: https://doi.org/10.1016/S1359-6454(97)00377-7
[eshelby1957determination]: https://doi.org/10.1098/rspa.1957.0133

## Parameterization and simulation conditions

This problem is solved in two dimensions to reduce computational costs, but note that we do not utilize symmetry to further reduce the problem size.  The matrix and precipitate phases have cubic symmetry, such that three independent elastic stiffnesses exist for each phase: $C_{1111}$, $C_{1122}$, and $C_{1212}$ [[1][nye1957physical]], and we take $C_{ijkl}^{\text{precip}}=1.1C_{ijkl}^{\text{matrix}}$.  In addition, the precipitate misfit strain takes the form $\epsilon^T_{11}=\epsilon^T_{22} > 0$, $\epsilon^T_{12}=0$.  Because this benchmark problem relies on the balance between interfacial and elastic energy, we use dimensional units of attojoules and nanometers.  The diffuse interface width is chosen as 5 nm for  $0.05 < \eta < 0.95$ and the interfacial energy is chosen as 50 aJ/nm$^2$ (equivalent to 50 mJ/m$^2$). The model parameters are given in the following table.

### Parameter values for all variants

| Quantity                      | Symbol                              | Value              |
|:------------------------------|:-----------------------------------:|--------------------|
| Gradient energy coefficient   | $\kappa$                            | 0.29 aJ/nm         |
| Well height                   | $w$                                 | 0.1 aJ/nm$^3$      |
| Mobility                      | $M$                                 | 5                  |
| Misfit strain                 | $\epsilon^T_{11}$=$\epsilon^T_{22}$ | 0.5 %              |
| Elastic stiffness matrix      | $C^{\text{matrix}}_{1111}$          | 250 aJ/nm$^3$      |
| Elastic stiffness matrix      | $C^{\text{matrix}}_{1122}$          | 150 aJ/nm$^3$      |
| Elastic stiffness matrix      | $C^{\text{matrix}}_{1212}$          | 100 aJ/nm$^3$      |

Note that 1 aJ/nm$^3$ is equivalent to 1 GPa.

We utilize both circular and elliptical initial precipitate shapes for a given initial precipitate area [[2][thompson1994equilibrium], [3][li2004two]]; all initial precipitates have a diffuse interface width of 5 nm. To have an equal area for an ellipse as a circle with radius $r$, we choose ellipse axes as $a_{[10]}=r/0.9$ and $a_{[01]}=0.9r$. Simulations are performed for two initial precipitate sizes: a smaller one with an area of $20^2 \pi \textrm{ nm}^2$ and a larger one with an area of $75^2 \pi \textrm{ nm}^2$. The center of each precipitate is embedded in the center of a square computational domain, which is given the coordinate (0,0).  The computational domain is $(400 \textrm{ nm})^2$ for the smaller precipitates and $(1500 \textrm{ nm})^2$ for the larger precipitates to allow long-range elastic fields to decay.  No-stress boundary conditions are applied for the displacements, and no-flux boundary conditions are applied for $\eta$. Because our implementation is based on solving for displacements rather than strain, we specify $u_{[10]}=0$ at the top, middle, and bottom of the $y=0$ axis ($e.g.,$ in the [01] direction) and $u_{[01]}=0$ at the top, middle, and bottom of the $x=0$ axis ($e.g.,$ in the [10] direction) to remove the nullspace in the solution.  Simulations are run until equilibrium is achieved.

The presence of elastic strain energy or a curved interface will increase the final value of $\eta$ in both the matrix and precipitate phases from the equilibrium value given by the common tangent of $f_{\text{bulk}}$.  In addition, the precipitate may change size during the course of the energy relaxation because of the shifting balance between the $f_{\text{bulk}}$ and $f_{\text{el}}$ energy contributions.  Because the precipitate volume within the computational domain is much smaller than that of the matrix, a precipitate may shrink entirely away in the process achieving the equilibrium value of $\eta$ in the matrix.  To avoid this, the initial value of $\eta$ in the matrix should be set slightly greater than zero.  For the simulations with the small particles, we set $\eta^{\text{matrix}}_0=0.0065$, while for the large particles, $\eta^{\text{matrix}}_0=0.005$. In addition, we set $\eta^{\text{precip}}_0=1$ for all simulations.

[nye1957physical]: https://doi.org/10.1029/EO064i045p00643-01
[thompson1994equilibrium]: https://doi.org/10.1016/0956-7151(94)90036-1
[li2004two]: https://doi.org/10.1016/j.actamat.2004.08.041

Overall, there are 8 different parameter variations for this problem. These are labeled (a) through (h).

### Parameter values for (a) through (h)

| Quantity                 | Symbol                     | Value (a)                 | Value (b)                 | Value (c)                 | Value (d)                 | Value (e)                 | Value (f)                 | Value (g)                 | Value (h)                 |
|:-------------------------|:--------------------------:|--------------------------:|--------------------------:|--------------------------:|--------------------------:|--------------------------:|--------------------------:|--------------------------:|--------------------------:|
| Radius                   | $r$                        | 20 $\textrm{nm}$          | 75 $\textrm{nm}$          | 20 $\textrm{nm}$          | 75 $\textrm{nm}$          | 20 $\textrm{nm}$          | 75 $\textrm{nm}$          | 20 $\textrm{nm}$          | 75 $\textrm{nm}$          |
| Eclipse axes (10)        | $a_{[10]}$                 | $r$                       | $r$                       | $r$                       | $r$                       | $r$ / 0.9                 | $r$ / 0.9                 | $r$ / 0.9                 | $r$ / 0.9                 |
| Eclipse axes (01)        | $a_{[01]}$                 | $r$                       | $r$                       | $r$                       | $r$                       | 0.9 $r$                   | 0.9 $r$                   | 0.9 $r$                   | 0.9 $r$                   |
| Precipitate area         |                            | 20$^2 \pi \textrm{ nm}^2$ | 75$^2 \pi \textrm{ nm}^2$ | 20$^2 \pi \textrm{ nm}^2$ | 75$^2 \pi \textrm{ nm}^2$ | 20$^2 \pi \textrm{ nm}^2$ | 75$^2 \pi \textrm{ nm}^2$ | 20$^2 \pi \textrm{ nm}^2$ | 75$^2 \pi \textrm{ nm}^2$ |
| Domain size              |                            | $\text{(400 nm)}^2$       | $\text{(1500 nm)}^2$      | $\text{(400 nm)}^2$       | $\text{(1500 nm)}^2$      | $\text{(400 nm)}^2$       | $\text{(1500 nm)}^2$      | $\text{(400 nm)}^2$       | $\text{(1500 nm)}^2$  |
| Order Parameter (matrix) | $\eta^{\text{matrix}}_0$   | 0.0065                    | 0.005                     | 0.0065                    | 0.005                     | 0.0065                    | 0.005                     | 0.0065                    | 0.005                     |
| Order Parameter (precip) | $\eta^{\text{precip}}_0$   | 1                         | 1                         | 1                         | 1                         | 1                         | 1                         | 1                         | 1                         |
| Elastic stiffness precip | $C^{\text{precip}}_{1111}$ | $\text{250 aJ/nm}^3$      | $\text{250 aJ/nm}^3$      | $\text{275 aJ/nm}^3$      | $\text{275 aJ/nm}^3$             | $\text{250 aJ/nm}^3$             | $\text{250 aJ/nm}^3$             | $\text{275 aJ/nm}^3$             | $\text{275 aJ/nm}^3$             |
| Elastic stiffness precip | $C^{\text{precip}}_{1122}$ | $\text{150 aJ/nm}^3$      | $\text{150 aJ/nm}^3$      | $\text{165 aJ/nm}^3$      | $\text{165 aJ/nm}^3$             | $\text{150 aJ/nm}^3$             | $\text{150 aJ/nm}^3$             | $\text{165 aJ/nm}^3$             | $\text{165 aJ/nm}^3$             |
| Elastic stiffness precip | $C^{\text{precip}}_{1212}$ | $\text{100 aJ/nm}^3$      | $\text{100 aJ/nm}^3$      | $\text{110 aJ/nm}^3$      | $\text{110 aJ/nm}^3$             | $\text{100 aJ/nm}^3$             | $\text{100 aJ/nm}^3$             | $\text{110 aJ/nm}^3$             | $\text{110 aJ/nm}^3$             |

## Example Result at Equilibrium

The final morphologies of the precipitates for problems (a), (c), (e) and (g). The dark pink curve is for variant (a) and (e) and the light pink is for variant (c) and (g). The results indicate that the shape of the initial precipitate does not influence the final shape of the precipitate for the smaller precipitate variant.

## Submission Guidelines

All benchmark solutions should be run to equilibrium. The following data should be collected for each upload.

 - Global quantities as the simulation evolves including

   * the total free energy, $\;\;\mathcal{F}\;\;$

   * the interfacial free energy, $\;\;\mathcal{F}_{\text{grad}}=\int_V \frac{\kappa}{2} |\nabla \eta|^2 \; dV\;\;$

   * the elastic free energy, $\;\;\mathcal{F}_{\text{el}} = \int_V f_{\text{el}} \; dV\;\;$

   * the area of the precipitate $\;\;\int_V \eta dV\;\;$ and

   * the precipitate lengths $a_{10}$, $a_{01}$ and $a_d$ measured from the center of the drop to the $\eta=0.5$ contour in the $x$ ([10]), $y$ ([01]) and diagonal directions, respectively. The angle used for the diagonal direction is given by $\theta_d$ such that  $\tan\theta_d=a_{01}/a_{10}$.

 - The $\eta=0.5$ level set contour position at equilibrium or the latest time step.

### Evolving Data Format

The evolving data should be stored in a CSV file with columns labeled as `time`, `a_01`, `a_10`, `a_d`,`elastic_free_energy`,`gradient_free_energy`, `precipitate_area` and `total_free_energy`. The CSV file should be formatted as a table and have the following form (note that the column ordering is inconsequential),

```
a_01,a_10,a_d,elastic_free_energy,gradient_free_energy,precipitate_area,time,total_free_energy
19.97429316515008,19.974293165149973,20.140688631434397,6.185957746168657,4.510418048831537,1264.0,0.1,17.72178588199252
19.86315763877582,19.863157638775874,20.098536436029132,6.054959230125162,2.9620862374085544,1264.0,1.1,17.207555522195793
19.906346454363486,19.906346454363465,20.134576060150618,6.021500927987024,2.736582255973086,1264.0,2.1,17.204113636263912
...
```

The data should be collected frequently during the simulation, but greater than 20 data points at a minimum (more than 1000 data points is unnecessary and won't improve resolution). The data should be named `all_data` in the "Short name of data" box located in the "Data Files" section of the [upload form]. The 2D radio button should be checked, the entry in the "Name of the x-axis column" box should be `time` and the entry in the "Name of y-axis column" should be `total_free_energy`. Only one "Data Files" section upload is required for the evolving data.

### Equilibrium Data Format

The equilibrium data should be stored in either a CSV file with columns labeled as `x` and `y`. The CSV file should be formatted as a table and have the following form (note that the column ordering is inconsequential),

```
x,y
-9.5,-18.615967564796016
-8.5,-18.84790477132558
-7.5,-19.030286708741155
-6.5,-19.158255175095714
```

The contour data should be in a sequence that enables an ordered traversal of the contour line. The data should be named `contour` in the "Short name of data" box located in the "Data Files" section of the [upload form]. The 2D radio button should be checked, the entry in the "Name of the x-axis column" box should be `x` and the entry in the "Name of y-axis column" should be `y`. Only one "Data Files" section upload is required for the evolving data.

Please use the [upload form] to upload your results.

[upload form]: ../../simulations/upload_form/

```python

```


# Markdown
Actually, the best way to make a notebook look nice ist to **extensively use markdown**. For example, we can generate a nice static table of contents like this:

1. [jupyter_contrib_nbextensions](#nbext)
    * [Table of contents 2](#toc2)
    * [LaTeX_envs](#tex)
    * [Exercise](#ex)
    * [Python in markdown](#mark)

2. [Interactive and animated plots](#plot)
    * [Data](#data)
    * [maplotlib notebook](#matnb)
    * [Animated plots](#ani)

3. [Nbconvert](#nbcon)

Links are generally written like this link to [nbextension.readthedocs](http://jupyter-contrib-nbextensions.readthedocs.io).

# jupyter_contrib_nbextensions
This PyMOTW is about [jupyter_contrib_nbextensions](https://github.com/ipython-contrib/jupyter_contrib_nbextensions), a repository containing a collection of extensions that add functionality to the Jupyter notebook. These extensions are mostly written in Javascript and will be loaded locally in your browser.

LINKS:
* https://github.com/ipython-contrib/jupyter_contrib_nbextensions
* http://jupyter-contrib-nbextensions.readthedocs.io

The notebook extensions can be selected and configured in your Jupyter home tree via the tab *Nbextensions* (http://localhost:8888/tree#nbextensions_configurator).

## Table of contents 2 (toc2)
The toc2 extension enables to collect all running headers and display them in a floating window, as a sidebar or with a navigation menu. The extension is also draggable, resizable, collapsable, dockable and features automatic numerotation with unique links ids, and an optional toc cell.

Have a look at the options in the Nbextensions tab. **NOTE:** When using toc2 you may not use `<a id='toc'></a>`!

### Conclusion
**I love this**. Simple and easy. Look at the sidebar table of contents. Neat, right? Let's you jump to different sections quite easy and also shows you which section is currently selected and which section is queued to run.

## LaTeX_envs

### Equations, `\ref{eq:}`, theorem environments
The following code
The dot-product is defined by equation (\ref{eq:dotp}) in theorem \ref{theo:dotp} just below:
\begin{theorem}[Dot Product] \label{theo:dotp}
Let $u$ and $v$ be two vectors of $\mathbb{R}^n$. The dot product can be expressed as
\begin{equation}
\label{eq:dotp}
u^Tv = |u||v| \cos \theta,
\end{equation}
where $\theta$ is the angle between $u$ and $v$ ...
\end{theorem}
renders like this:

The dot-product is defined by equation (\ref{eq:dotp}) in theorem \ref{theo:dotp} just below:
\begin{theorem}[Dot Product] \label{theo:dotp}
Let $u$ and $v$ be two vectors of $\mathbb{R}^n$. The dot product can be expressed as
\begin{equation}
\label{eq:dotp}
u^Tv = |u||v| \cos \theta,
\end{equation}
where $\theta$ is the angle between $u$ and $v$ ...
\end{theorem}

### Citations, bibliography and references
It seems, that you can place a `.bib` file in the same directory as the notebook and use `\cite{}` to generate citations. Let's see if it works: \cite{Thomson1887}, \cite{Hebb1949}

Unfortunately, it does not really work for me, maybe because nbextensions said that latex_envs was *possibly incompatible*. My output after clicking *Read bibliography and generate references section* is the following:

#### References

[Thomson1887] !! _This reference was not found in library.bib _ !!

[Hebb1949] !! _This reference was not found in library.bib _ !!

### Conclusions
Probably awesome if it works. Especially citations would be great to have. However, I don't know if I really need the environments. Math works alright in markdown also without this extension.

## Exercise and Exercise2
These are two extensions for Jupyter, for hiding/showing solutions cells.

### Exercise
\begin{exercise}[Laplace Equation]
Write the Laplace equation $\Delta\Phi=0$ in spherical coordinates.
\end{exercise}

SOLUTION

\begin{equation}
\frac{1}{r^2}\frac{\partial}{\partial r} \left( r^2 \frac{\partial}{\partial r} \Phi \right) + \frac{1}{r^2 \sin\theta}\frac{\partial}{\partial \theta} \left( \sin\theta \frac{\partial}{\partial \theta} \Phi \right) + \frac{1}{r^2 \sin^2\theta}\frac{\partial}{\partial \phi^2} \Phi
\end{equation}

### Exercise2
\begin{exercise}[Laplace Equation]
Write the Laplace equation $\Delta\Phi=0$ in spherical coordinates.
\end{exercise}

SOLUTION

\begin{equation}
\frac{1}{r^2}\frac{\partial}{\partial r} \left( r^2 \frac{\partial}{\partial r} \Phi \right) + \frac{1}{r^2 \sin\theta}\frac{\partial}{\partial \theta} \left( \sin\theta \frac{\partial}{\partial \theta} \Phi \right) + \frac{1}{r^2 \sin^2\theta}\frac{\partial}{\partial \phi^2} \Phi
\end{equation}

### Conclusions
I encounter some bugs with Exercise2, the solution shows up after reloading the notebook. Therefore the winner is Exercise! Could be useful for workshops..

## Python in markdown

```python
a=3.765
```

Print the value of the variable in markdown using
a={{a}}
a={{a}}

### Conclusion
 `¯\_(ツ)_/¯`

# Interactive and animated plots
## Some random data to plot

```python
import numpy as np
import elephant
import neo
import quantities as pq
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from IPython.display import HTML
import matplotlib.animation as animation
```

    /home/papen/.local/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
      from ._conv import register_converters as _register_converters

```python
# Load block from ANDA data
block = np.load('/home/papen/git_repos/ANDA2017/data/data2.npy').item()

# Get spiketrain of first regular trial
sts = []
idx = block.annotations['all_trial_ids']
sts.append(block.filter(targdict={'trial_id': idx[0]}, objects=neo.Segment)[0].spiketrains)

# Generate binned time series of spike counts and apply PCA
binsize = 100*pq.ms
binned = elephant.conversion.BinnedSpikeTrain(sts[0], binsize=binsize).to_array()
pca = PCA(n_components=3)
pca.fit(binned.T)
PC  = np.matmul(pca.components_,binned)[:3,:]
t = np.arange(len(PC[0,:]))*binsize
```

## Matplotlib notebook
Using `%matplotlib notebook` activates the nbagg backend added in matplotlib 1.4, which will include a javascript interface for interaction with inline figures in the notebook. Apparently for Python2 one can also use `%matplotlib nbagg`.

```python
%matplotlib notebook
```

```python
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(121, projection='3d')
ax.plot(PC[0,:], PC[1,:], PC[2,:], '-k')
ax.set_xlabel('Comp. 1')
ax.set_ylabel('Comp. 2')
ax.set_zlabel('Comp. 3')

ax = fig.add_subplot(122)
for i in xrange(len(PC[:,0])):
    ax.plot(t, PC[i,:], '-', label='PC {}'.format(i))
ax.set_xlabel('time [ms]')
ax.set_ylabel('PC')
plt.legend()
```

    <IPython.core.display.Javascript object>

    <matplotlib.legend.Legend at 0x7fc69d7aab50>

## Animated plots with HTML

The following example is taken from https://matplotlib.org/examples/animation/simple_anim.html

```python
%matplotlib inline

def test(binned):
    fig = plt.figure()
    ax = fig.add_subplot(111)

    line, = ax.plot(t, binned[0,:])

    def animate(i, title_num=None):
        line.set_ydata(binned[i,:])  # update the data
        ax = plt.gca()
        ax.set_title('Spike counts of unit {}'.format(i))
        return line,

    # Init only required for blitting to give a clean slate.
    def init():
        line.set_ydata([])
        return line,

    ani = animation.FuncAnimation(fig, animate, np.arange(1, len(binned[:,0])), init_func=init,
                                  interval=500, blit=True)
    return ani, ax

ani, ax = test(binned)

ax.set_xlabel('time [ms]')
ax.set_ylabel('spike count')

HTML(ani.to_html5_video())
```

# Nbconvert
Detailed information can be found here: https://github.com/jupyter/nbconvert

In order to cenvert to LaTeX, use:

`jupyter nbconvert --to latex_with_lenvs --LenvsLatexExporter.removeHeaders=True PyMOTW_nbextensions.ipynb`

You can also convert your notebook to a prsentation, e.g. using reveal.js like this (Note, first you need to arrange slides by clicking View -> Cell -> Slideshow):

`jupyter nbconvert --to slides --post serve PyMOTW_nbextensions.ipynb`

```python

```