# Neuromatch Academy: Week 1, Day 5, Tutorial 2
# Dimensionality Reduction: Principal component analysis

__Content creators:__ Alex Cayco Gajic, John Murray

__Content reviewers:__ Roozbeh Farhoudi, Matt Krause, Spiros Chavlis, Richard Gao, Michael Waskom

---
# Tutorial Objectives

In this notebook we'll learn how to perform PCA by projecting the data onto the eigenvectors of its covariance matrix.

Overview:
- Calculate the eigenvectors of the sample covariance matrix.
- Perform PCA by projecting data onto the eigenvectors of the covariance matrix.
- Plot and explore the eigenvalues.

To quickly refresh your knowledge of eigenvalues and eigenvectors, you can watch this [short video](https://www.youtube.com/watch?v=kwA3qM0rm7c) (4 minutes) for a geometrical explanation. For a deeper understanding, this [in-depth video](https://www.youtube.com/watch?v=PFDu9oVAE-g&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab&index=14) (17 minutes) provides an excellent basis and is beautifully illustrated.

```python
# @title Video 1: PCA
from IPython.display import YouTubeVideo
video = YouTubeVideo(id="-f6T9--oM0E", width=854, height=480, fs=1)
print("Video available at https://youtube.com/watch?v=" + video.id)
video
```

    Video available at https://youtube.com/watch?v=-f6T9--oM0E

---
# Setup
Run these cells to get the tutorial started.

```python
# Imports
import numpy as np
import matplotlib.pyplot as plt
```

```python
# @title Figure Settings
import ipywidgets as widgets  # interactive display
%config InlineBackend.figure_format = 'retina'
plt.style.use("https://raw.githubusercontent.com/NeuromatchAcademy/course-content/master/nma.mplstyle")
```

```python
# @title Helper functions

def plot_eigenvalues(evals):
  """
  Plots eigenvalues.

  Args:
      (numpy array of floats) : Vector of eigenvalues

  Returns:
    Nothing.

  """
  plt.figure(figsize=(4, 4))
  plt.plot(np.arange(1, len(evals) + 1), evals, 'o-k')
  plt.xlabel('Component')
  plt.ylabel('Eigenvalue')
  plt.title('Scree plot')
  plt.xticks(np.arange(1, len(evals) + 1))
  plt.ylim([0, 2.5])

def sort_evals_descending(evals, evectors):
  """
  Sorts eigenvalues and eigenvectors in decreasing order. Also aligns first two
  eigenvectors to be in first two quadrants (if 2D).

  Args:
    evals (numpy array of floats)    : Vector of eigenvalues
    evectors (numpy array of floats) : Corresponding matrix of eigenvectors
                                        each column corresponds to a different
                                        eigenvalue

  Returns:
    (numpy array of floats)          : Vector of eigenvalues after sorting
    (numpy array of floats)          : Matrix of eigenvectors after sorting
  """

  index = np.flip(np.argsort(evals))
  evals = evals[index]
  evectors = evectors[:, index]
  if evals.shape[0] == 2:
    if np.arccos(np.matmul(evectors[:, 0],
                           1 / np.sqrt(2) * np.array([1, 1]))) > np.pi / 2:
      evectors[:, 0] = -evectors[:, 0]
    if np.arccos(np.matmul(evectors[:, 1],
                           1 / np.sqrt(2) * np.array([-1, 1]))) > np.pi / 2:
      evectors[:, 1] = -evectors[:, 1]
  return evals, evectors

def plot_data(X):
  """
  Plots bivariate data. Includes a plot of each random variable, and a scatter
  scatter plot of their joint activity. The title indicates the sample
  correlation calculated from the data.

  Args:
    X (numpy array of floats) : Data matrix each column corresponds to a
                                different random variable

  Returns:
    Nothing.
  """

  fig = plt.figure(figsize=[8, 4])
  gs = fig.add_gridspec(2, 2)
  ax1 = fig.add_subplot(gs[0, 0])
  ax1.plot(X[:, 0], color='k')
  plt.ylabel('Neuron 1')
  ax2 = fig.add_subplot(gs[1, 0])
  ax2.plot(X[:, 1], color='k')
  plt.xlabel('Sample Number (sorted)')
  plt.ylabel('Neuron 2')
  ax3 = fig.add_subplot(gs[:, 1])
  ax3.plot(X[:, 0], X[:, 1], '.', markerfacecolor=[.5, .5, .5],
           markeredgewidth=0)
  ax3.axis('equal')
  plt.xlabel('Neuron 1 activity')
  plt.ylabel('Neuron 2 activity')
  plt.title('Sample corr: {:.1f}'.format(np.corrcoef(X[:, 0], X[:, 1])[0, 1]))
  plt.show()

def get_data(cov_matrix):
  """
  Returns a matrix of 1000 samples from a bivariate, zero-mean Gaussian

  Note that samples are sorted in ascending order for the first random
  variable.

  Args:
    var_1 (scalar)                     : variance of the first random variable
    var_2 (scalar)                     : variance of the second random variable
    cov_matrix (numpy array of floats) : desired covariance matrix

  Returns:
    (numpy array of floats)            : samples from the bivariate Gaussian,
                                          with each column corresponding to a
                                          different random variable
  """

  mean = np.array([0, 0])
  X = np.random.multivariate_normal(mean, cov_matrix, size=1000)
  indices_for_sorting = np.argsort(X[:, 0])
  X = X[indices_for_sorting, :]
  return X

def calculate_cov_matrix(var_1, var_2, corr_coef):
  """
  Calculates the covariance matrix based on the variances and
  correlation coefficient.

  Args:
    var_1 (scalar)         :  variance of the first random variable
    var_2 (scalar)         :  variance of the second random variable
    corr_coef (scalar)     :  correlation coefficient

  Returns:
    (numpy array of floats) : covariance matrix
  """
  cov = corr_coef * np.sqrt(var_1 * var_2)
  cov_matrix = np.array([[var_1, cov], [cov, var_2]])
  return cov_matrix

def define_orthonormal_basis(u):
  """
  Calculates an orthonormal basis given an arbitrary vector u.

  Args:
    u (numpy array of floats) : arbitrary 2D vector used for new basis

  Returns:
    (numpy array of floats)   : new orthonormal basis columns correspond to
                                basis vectors
  """

  u = u / np.sqrt(u[0] ** 2 + u[1] ** 2)
  w = np.array([-u[1], u[0]])
  W = np.column_stack((u, w))
  return W

def plot_data_new_basis(Y):
  """
  Plots bivariate data after transformation to new bases. Similar to plot_data
  but with colors corresponding to projections onto basis 1 (red) and
  basis 2 (blue).
  The title indicates the sample correlation calculated from the data.

  Note that samples are re-sorted in ascending order for the first random
  variable.

  Args:
    Y (numpy array of floats) : Data matrix in new basis each column
                                corresponds to a different random variable

  Returns:
    Nothing.
  """

  fig = plt.figure(figsize=[8, 4])
  gs = fig.add_gridspec(2, 2)
  ax1 = fig.add_subplot(gs[0, 0])
  ax1.plot(Y[:, 0], 'r')
  plt.ylabel('Projection \n basis vector 1')
  ax2 = fig.add_subplot(gs[1, 0])
  ax2.plot(Y[:, 1], 'b')
  plt.xlabel('Projection \n basis vector 1')
  plt.ylabel('Projection \n basis vector 2')
  ax3 = fig.add_subplot(gs[:, 1])
  ax3.plot(Y[:, 0], Y[:, 1], '.', color=[.5, .5, .5])
  ax3.axis('equal')
  plt.xlabel('Projection basis vector 1')
  plt.ylabel('Projection basis vector 2')
  plt.title('Sample corr: {:.1f}'.format(np.corrcoef(Y[:, 0], Y[:, 1])[0, 1]))
  plt.show()

def change_of_basis(X, W):
  """
  Projects data onto a new basis.

  Args:
    X (numpy array of floats) : Data matrix each column corresponding to a
                                different random variable
    W (numpy array of floats) : new orthonormal basis columns correspond to
                                basis vectors

  Returns:
    (numpy array of floats)   : Data matrix expressed in new basis
  """

  Y = np.matmul(X, W)
  return Y

def plot_basis_vectors(X, W):
  """
  Plots bivariate data as well as new basis vectors.

  Args:
    X (numpy array of floats) : Data matrix each column corresponds to a
                                different random variable
    W (numpy array of floats) : Square matrix representing new orthonormal
                                basis each column represents a basis vector

  Returns:
    Nothing.
  """

  plt.figure(figsize=[4, 4])
  plt.plot(X[:, 0], X[:, 1], '.', color=[.5, .5, .5], label='Data')
  plt.axis('equal')
  plt.xlabel('Neuron 1 activity')
  plt.ylabel('Neuron 2 activity')
  plt.plot([0, W[0, 0]], [0, W[1, 0]], color='r', linewidth=3,
           label='Basis vector 1')
  plt.plot([0, W[0, 1]], [0, W[1, 1]], color='b', linewidth=3,
           label='Basis vector 2')
  plt.legend()
  plt.show()

```

---
# Section 1: Calculate the eigenvectors of the the sample covariance matrix

As we saw in the lecture, PCA represents data in a new orthonormal basis defined by the eigenvectors of the covariance matrix. Remember that in the previous tutorial, we generated bivariate normal data with a specified covariance matrix $\bf \Sigma$, whose $(i,j)$th element is:
\begin{equation}
\Sigma_{ij} = E[ x_i x_j ] - E[ x_i] E[ x_j ] .
\end{equation}
However, in real life we don't have access to this ground-truth covariance matrix. To get around this, we can use the sample covariance matrix, $\bf\hat\Sigma$, which is calculated directly from the data. The $(i,j)$th element of the sample covariance matrix is:
\begin{equation}
 \hat \Sigma_{ij} =  \frac{1}{N_\text{samples}}{\bf x}_i^T {\bf x}_j - \bar {\bf x}_i \bar{\bf x}_j ,
\end{equation}
where ${\bf x}_i = [ x_i(1), x_i(2), \dots,x_i(N_\text{samples})]^T$ is a column vector representing all measurements of neuron $i$, and  $\bar {\bf x}_i$ is the mean of neuron $i$ across samples:
\begin{equation}
\bar {\bf x}_i = \frac{1}{N_\text{samples}} \sum_{k=1}^{N_\text{samples}} x_i(k).
\end{equation}
If we assume that the data has already been mean-subtracted, then we can write the sample covariance matrix in a much simpler matrix form:
\begin{align}
{\bf \hat \Sigma}
&= \frac{1}{N_\text{samples}} {\bf X}^T {\bf X}.
\end{align}

## Exercise 1: Calculation of the covariance matrix

Before calculating the eigenvectors, you must first calculate the sample covariance matrix.

**Steps**
* Complete the function `get_sample_cov_matrix` by first subtracting the sample mean of the data, then calculate $\bf \hat \Sigma$ using the equation above.
* Use `get_data` to generate bivariate normal data, and calculate the sample covariance matrix with your finished `get_sample_cov_matrix`. Compare this estimate to the true covariate matrix using `calculate_cov_matrix`.

```python
help(get_data)
help(calculate_cov_matrix)
```

    Help on function get_data in module __main__:

    get_data(cov_matrix)
        Returns a matrix of 1000 samples from a bivariate, zero-mean Gaussian

        Note that samples are sorted in ascending order for the first random
        variable.

        Args:
          var_1 (scalar)                     : variance of the first random variable
          var_2 (scalar)                     : variance of the second random variable
          cov_matrix (numpy array of floats) : desired covariance matrix

        Returns:
          (numpy array of floats)            : samples from the bivariate Gaussian,
                                                with each column corresponding to a
                                                different random variable

    Help on function calculate_cov_matrix in module __main__:

    calculate_cov_matrix(var_1, var_2, corr_coef)
        Calculates the covariance matrix based on the variances and
        correlation coefficient.

        Args:
          var_1 (scalar)         :  variance of the first random variable
          var_2 (scalar)         :  variance of the second random variable
          corr_coef (scalar)     :  correlation coefficient

        Returns:
          (numpy array of floats) : covariance matrix

```python
def get_sample_cov_matrix(X):
  """
  Returns the sample covariance matrix of data X

  Args:
    X (numpy array of floats) : Data matrix each column corresponds to a
                                different random variable

  Returns:
    (numpy array of floats)   : Covariance matrix
  """

  #################################################
  ## TODO for students: calculate the covariance matrix
  # Fill out function and remove
  raise NotImplementedError("Student excercise: calculate the covariance matrix!")
  #################################################

  # Subtract the mean of X
  X = ...
  # Calculate the covariance matrix (hint: use np.matmul)
  cov_matrix = ...

  return cov_matrix

##########################################################
## TODO for students: generate bivariate Gaussian data
# with variances of 1 and a correlation coefficient of 0.8
# compare the true and sample covariance matrices
##########################################################
variance_1 = 1
variance_2 = 1
corr_coef = 0.8

np.random.seed(2020)  # set random seed
# Uncomment below code to test your function
# cov_matrix = calculate_cov_matrix(variance_1, variance_2, corr_coef)
# print(cov_matrix)

# X = get_data(cov_matrix)
# sample_cov_matrix = get_sample_cov_matrix(X)
# print(sample_cov_matrix)
```

SAMPLE OUTPUT
```
[[1.  0.8]
 [0.8 1. ]]
[[0.99315313 0.82347589]
 [0.82347589 1.01281397]]
```

```python
# to_remove solution

def get_sample_cov_matrix(X):
  """
  Returns the sample covariance matrix of data X

  Args:
    X (numpy array of floats) : Data matrix each column corresponds to a
                                different random variable

  Returns:
    (numpy array of floats)   : Covariance matrix
  """

  # Subtract the mean of X
  X = X - np.mean(X, 0)
  # Calculate the covariance matrix (hint: use np.matmul)
  cov_matrix = cov_matrix = 1 / X.shape[0] * np.matmul(X.T, X)

  return cov_matrix

variance_1 = 1
variance_2 = 1
corr_coef = 0.8

np.random.seed(2020)  # set random seed
cov_matrix = calculate_cov_matrix(variance_1, variance_2, corr_coef)
print(cov_matrix)

X = get_data(cov_matrix)
sample_cov_matrix = get_sample_cov_matrix(X)
print(sample_cov_matrix)
```

    [[1.  0.8]
     [0.8 1. ]]
    [[0.99315313 0.82347589]
     [0.82347589 1.01281397]]

## Exercise 2: Eigenvectors of the Covariance matrix

Next you will calculate the eigenvectors of the covariance matrix. Plot them along with the data to check that they align with the geometry of the data.

**Steps:**
* Calculate the eigenvalues and eigenvectors of the sample covariance matrix. (**Hint:** use `np.linalg.eigh`, which finds the eigenvalues of a symmetric matrix).
* Use the provided code to sort the eigenvalues in descending order.
* Plot the eigenvectors on a scatter plot of the data, using the function `plot_basis_vectors`.

```python
help(sort_evals_descending)
help(plot_basis_vectors)
```

    Help on function sort_evals_descending in module __main__:

    sort_evals_descending(evals, evectors)
        Sorts eigenvalues and eigenvectors in decreasing order. Also aligns first two
        eigenvectors to be in first two quadrants (if 2D).

        Args:
          evals (numpy array of floats)    : Vector of eigenvalues
          evectors (numpy array of floats) : Corresponding matrix of eigenvectors
                                              each column corresponds to a different
                                              eigenvalue

        Returns:
          (numpy array of floats)          : Vector of eigenvalues after sorting
          (numpy array of floats)          : Matrix of eigenvectors after sorting

    Help on function plot_basis_vectors in module __main__:

    plot_basis_vectors(X, W)
        Plots bivariate data as well as new basis vectors.

        Args:
          X (numpy array of floats) : Data matrix each column corresponds to a
                                      different random variable
          W (numpy array of floats) : Square matrix representing new orthonormal
                                      basis each column represents a basis vector

        Returns:
          Nothing.

```python
#################################################
## TO DO for students: Calculate and sort the eigenvalues in descending order
#################################################
# Calculate the eigenvalues and eigenvectors
# evals, evectors = ...
# Sort the eigenvalues in descending order
# evals, evectors = ...

# plot_basis_vectors(X, evectors)
```

```python
# to_remove solution
# Calculate the eigenvalues and eigenvectors
evals, evectors = np.linalg.eigh(cov_matrix)
# Sort the eigenvalues in descending order
evals, evectors = sort_evals_descending(evals, evectors)

with plt.xkcd():
  plot_basis_vectors(X, evectors)
```

---
# Section 2: Perform PCA by projecting data onto the eigenvectors

To perform PCA, we will project the data onto the eigenvectors of the covariance matrix, i.e.:
\begin{equation}
\bf S = X W
\end{equation}
where $\bf S$ is an $N_\text{samples} \times N$ matrix representing the projected data (also called *scores*), and $W$ is an $N\times N$ orthogonal matrix, each of whose columns represents the eigenvectors of the covariance matrix (also called *weights* or *loadings*).

## Exercise 3: PCA implementation

You will now perform PCA on the data using the intuition you have developed so far. Fill in the function below to carry out the steps to perform PCA by projecting the data onto the eigenvectors of its covariance matrix.

**Steps:**
* First subtract the mean.
* Then calculate the sample covariance matrix.
* Then find the eigenvalues and eigenvectors and sort them in descending order.
* Finally project the mean-centered data onto the eigenvectors.

```python
help(change_of_basis)
help(plot_data_new_basis)
```

    Help on function change_of_basis in module __main__:

    change_of_basis(X, W)
        Projects data onto a new basis.

        Args:
          X (numpy array of floats) : Data matrix each column corresponding to a
                                      different random variable
          W (numpy array of floats) : new orthonormal basis columns correspond to
                                      basis vectors

        Returns:
          (numpy array of floats)   : Data matrix expressed in new basis

    Help on function plot_data_new_basis in module __main__:

    plot_data_new_basis(Y)
        Plots bivariate data after transformation to new bases. Similar to plot_data
        but with colors corresponding to projections onto basis 1 (red) and
        basis 2 (blue).
        The title indicates the sample correlation calculated from the data.

        Note that samples are re-sorted in ascending order for the first random
        variable.

        Args:
          Y (numpy array of floats) : Data matrix in new basis each column
                                      corresponds to a different random variable

        Returns:
          Nothing.

```python
def pca(X):
  """
  Sorts eigenvalues and eigenvectors in decreasing order.

  Args:
    X (numpy array of floats): Data matrix each column corresponds to a
                               different random variable

  Returns:
    (numpy array of floats)  : Data projected onto the new basis
    (numpy array of floats)  : Vector of eigenvalues
    (numpy array of floats)  : Corresponding matrix of eigenvectors

  """

  #################################################
  ## TODO for students: calculate the covariance matrix
  # Fill out function and remove
  raise NotImplementedError("Student excercise: sort eigenvalues/eigenvectors!")
  #################################################

  # Subtract the mean of X
  X = ...
  # Calculate the sample covariance matrix
  cov_matrix = ...
  # Calculate the eigenvalues and eigenvectors
  evals, evectors = ...
  # Sort the eigenvalues in descending order
  evals, evectors = ...
  # Project the data onto the new eigenvector basis
  score = ...

  return score, evectors, evals

#################################################
## TODO for students: Call the function to calculate the eigenvectors/eigenvalues
#################################################

# Perform PCA on the data matrix X
# score, evectors, evals = ...
# Plot the data projected into the new basis
# plot_data_new_basis(score)
```

```python
# to_remove solution
def pca(X):
  """
  Performs PCA on multivariate data.

  Args:
    X (numpy array of floats) : Data matrix each column corresponds to a
                                different random variable

  Returns:
    (numpy array of floats)   : Data projected onto the new basis
    (numpy array of floats)   : Vector of eigenvalues
    (numpy array of floats)   : Corresponding matrix of eigenvectors

  """

  # Subtract the mean of X
  X = X - np.mean(X, axis=0)
  # Calculate the sample covariance matrix
  cov_matrix = get_sample_cov_matrix(X)
  # Calculate the eigenvalues and eigenvectors
  evals, evectors = np.linalg.eigh(cov_matrix)
  # Sort the eigenvalues in descending order
  evals, evectors = sort_evals_descending(evals, evectors)
  # Project the data onto the new eigenvector basis
  score = change_of_basis(X, evectors)

  return score, evectors, evals

# Perform PCA on the data matrix X
score, evectors, evals = pca(X)
# Plot the data projected into the new basis
with plt.xkcd():
  plot_data_new_basis(score)
```

## Plot and explore the eigenvalues

Finally, we will examine the eigenvalues of the covariance matrix. Remember that each eigenvalue describes the variance of the data projected onto its corresponding eigenvector. This is an important concept because it allows us to rank the PCA basis vectors based on how much variance each one can capture. First run the code below to plot the eigenvalues (sometimes called the "scree plot"). Which eigenvalue is larger?

```python
plot_eigenvalues(evals)
```

## Interactive Demo: Exploration of the correlation coefficient

Run the following cell and use the slider to change the correlation coefficient in the data. You should see the scree plot and the plot of basis vectors update.

**Questions:**
* What happens to the eigenvalues as you change the correlation coefficient?
* Can you find a value for which both eigenvalues are equal?
* Can you find a value for which only one eigenvalue is nonzero?

```python
# @title

# @markdown Make sure you execute this cell to enable the widget!

def refresh(corr_coef=.8):
  cov_matrix = calculate_cov_matrix(variance_1, variance_2, corr_coef)
  X = get_data(cov_matrix)
  score, evectors, evals = pca(X)
  plot_eigenvalues(evals)
  plot_basis_vectors(X, evectors)

_ = widgets.interact(refresh, corr_coef=(-1, 1, .1))
```

---
# Summary
- In this tutorial, we learned that goal of PCA is to find an orthonormal basis capturing the directions of maximum variance of the data. More precisely, the $i$th basis vector is the direction that maximizes the projected variance, while being orthogonal to all previous basis vectors. Mathematically, these basis vectors are the eigenvectors of the covariance matrix (also called *loadings*).
- PCA also has the useful property that the projected data (*scores*) are uncorrelated.
- The projected variance along each basis vector is given by its corresponding eigenvalue. This is important because it allows us rank the "importance" of each basis vector in terms of how much of the data variability it explains. An eigenvalue of zero means there is no variation along that direction so it can be dropped without losing any information about the original data.
- In the next tutorial, we will use this property to reduce the dimensionality of high-dimensional data.

---
# Bonus: Mathematical basis of PCA properties

```python
# @title Video 2: Properties of PCA
from IPython.display import YouTubeVideo
video = YouTubeVideo(id="p56UrMRt6-U", width=854, height=480, fs=1)
print("Video available at https://youtube.com/watch?v=" + video.id)
video
```

    Video available at https://youtube.com/watch?v=p56UrMRt6-U


```julia
using Plots, LaTeXStrings, Interact;
```

<div id="interact-js-shim">

</div>

# A32 Landau Theorie des Van der Waals Gases

Bei gegebenem $T,P$ wird im Gleichgewicht dasjenige (spezifische) Volumen $V(T,P)$ bzw. $v(T,P)$ angenommen, welches die __Gibbs'sche Freie Energie__ $G$ minimiert. Da per Definition

\begin{equation} G(T,P) = F(T,V(T,P)) + PV(T,P) \end{equation}

gilt, können wir auch schreiben

$$G(T,P) = \min_V \left\{ F(T,V) + PV \right\}$$

Das motiviert nach den Minima der variationellen freien Energie $F+PV$ zu suchen. Um das Verhalten in der Nähe des kritischen Punktes zu untersuchen, bietet sich eine Taylorentwicklung nach Potentenzen von $\Delta v = \tfrac{v-v_c}{v_c}$ an. Aus der Vorlesung wissen wir, dass das kritische spezifische Volumen durch $v_c = 3b$ und die krit. Temp. durch $T_c=8a/27b = 9a/8v_c$ gegeben ist.

---

Koeffizienten, Freie Energie und kritischer Druck als Funktionen von Temperatur, Druck, kritischem Druck/Temperatur.

Setze standardmäßig $T_c = P_c = 1$.

```julia
A(p,T;vc=1,Tc=1) = p*vc + 9/8*Tc - 3/2*T;
```

```julia
B(T;vc=1,Tc=1) = 9/8*(T-Tc);
```

```julia
C(T;vc=1,Tc=1) = (9/8)^2*T - 9/8*Tc;
```

```julia
# variationelle Gibbsche freie Energie pro Teilchen
g(dv,p,T;vc=1,Tc=1) = A(p,T;vc=vc,Tc=Tc)*dv + B(T;vc=vc,Tc=Tc)*(dv^2-dv^3) + C(T;vc=vc,Tc=Tc)*dv^4;
# 1. und 2. Ableitung
dg(dv,p,T;vc=1,Tc=1) = A(p,T;vc=vc,Tc=Tc) + B(T;vc=vc,Tc=Tc)*(2*dv-3*dv^2) + 4*C(T;vc=vc,Tc=Tc)*dv^3;
ddg(dv,p,T;vc=1,Tc=1) = B(T;vc=vc,Tc=Tc)*(2-6*dv) + 12*C(T;vc=vc,Tc=Tc)*dv^2;
```

```julia
Pc(vc=1,Tc=1) = 3/8*Tc/vc;
```

---

### Druck interaktiv verändern

```julia
# Setze Parameter
a=2
b=1.5
vc=3*b
Tc=8/27*a/b

# Definiere Bereiche zum Plotten
vv = linspace(-4,3,200)
TT = [10/11*Tc, Tc, 11/10*Tc]
lwT = [2 4 2]
lsT = [:dash :solid :dash]
#Waehle Plotly backend => Interaktive Plots.
plotly(size=(1024,768));
@manipulate for p in Pc(vc,Tc) *(1/10:0.01:3/2)
    plot(vv,
      [dv->g(dv,p,T,vc=vc,Tc=Tc)+1/4 for T in TT],
       ylim=(-1/2,1/2),
       xlabel=L"$\Delta v$",
       ylabel="g",
       lab=[join(["T=",rationalize(T/Tc),"*Tc"],"") for T in TT'],
        linewidth = lwT,
        linestyle = lsT
    )
    plot!(vv,
        [dv->dg(dv,p,T,vc=vc,Tc=Tc) for T in [Tc]],
        lab="g'@Tc"
    )
    plot!([cbrt(-A(p,Tc;vc=vc,Tc=Tc)/4/C(Tc;vc=vc,Tc=Tc))],[0],line=false,marker=(:circle,5,:red),lab="")
end
```

### Animation erstellen und als GIF speichern.
* ImageMagick ist installiert und im Pfad.

__oder__

* Julia Paket ImageMagick.jl ist installiert. Dann bitte die folgende Zelle ausführen

```julia
using ImageMagick
```

```julia
# Setze Parameter
a=2
b=1.5
vc=3*b
Tc=8/27*a/b

# Definiere Bereiche zum Plotten
vv = linspace(-4,3,200)
TT = [10/11*Tc, Tc, 11/10*Tc]
PP = [1/2, 3/4, 1., 3/2]
pyplot();
plt = [(:ylim,(-1/2,1/2)),
       (:xlabel,L"$\Delta v$"),
       (:ylabel,"g"),
       (:linewidth,[2 4 2]),
       (:linestyle, [:dash :solid :dash])
]
anim = @animate for p in Pc(vc,Tc)*(1/2:0.01:3/2)
    plot(vv,
      [dv->g(dv,p,T,vc=vc,Tc=Tc)+1/4 for T in TT],
        legend=false
       ; plt...
    )
#     plot!(vv,
#         [dv->dg(dv,p,T,vc=vc,Tc=Tc) for T in [Tc]]
#     )
    plot!([cbrt(-A(p,Tc;vc=vc,Tc=Tc)/4/C(Tc;vc=vc,Tc=Tc))],[0],line=false,marker=(:circle,5,:red))
    plot!([cbrt(-A(p,Tc;vc=vc,Tc=Tc)/4/C(Tc;vc=vc,Tc=Tc))],dv->ddg(dv,p,Tc,vc=vc,Tc=Tc),line=false,marker=(:star,5,:green))

end
gif(anim, "vdw.gif", fps=15)
```

    INFO: Saved animation to /Network/Servers/mac25.thp.uni-koeln.de/Volumes/RAID/skleinbo/Documents/Teaching/StatMech2016/vdw.gif

" />

### Statische Plots

```julia
a=2
b=1.5
vc=3*b
Tc=8/27*a/b

vv = linspace(-4,2,200) # Volumina
TT = [95/100*Tc,Tc,11/10*Tc] #Temperaturen

pyplot();
plt = plot(layout=4,ylim=(-1/2,1/2),size=(1280,1024),
    xlabel=L"$\Delta v$",
    ylabel="g",
    grid=false,
    )

for p in enumerate([1/2,3/4,1.,3/2])
    plot!(plt[p[1]],
        vv,[dv->g(dv,p[2]*Pc(vc,Tc),T,vc=vc,Tc=Tc)+1/4 for T in TT],
        lab=[ join(["(p/pc=",rationalize(p[2]),", T/Tc=",rationalize(T/Tc),")"],"") for T in TT'],
        linecolor=:black,
        linestyle=[:dot :solid :dash]
        )
end
display(plt)
```

# A33 Landau Theorie des Ising Ferromagneten

Das Vorgehen ist analog zum vdW-Gas-Flüssig-Übergang. Man entwickelt das entsprechende Potential - hier die _Helmholtz Freie Energie_ $F(T,h) = E - TS$ - in Potenzen des Ordungsparameters am kritischen Punkt. Hier erwartet man dass am kritischen Punkt die Magnetisierung verschwindet.

Im Allgemeinen benötigt man Kenntnis der kanonische Zustandssumme um die Energie zu berechnen. Bis auf Ausnahmen ist das für wechselwirkende Systeme nicht exakt machbar und man muss sich Näherungsmethoden bedienen. Eine davon ist die _Mean-Field-Methode_. Dabei wird die paarweise Wechselwirkung ersetzt durch ein gemitteltes effektives Feld. Dadurch kommt man zu einem lösbaren _Ein_-teilchen Problem

Konkret nähert man die Wechselwirkung

$$ s_i\left (h + \sum_j Js_j\right ) $$

indem man die Spins $s_j$ - die ja im Prinzip fluktuieren! - durch ihren fixierten Mittelwert ersetzt

$$ s_j \rightarrow \langle s_j \rangle = m. $$

Dabei haben wir ausserdem angenommen, dass sich im Gleichgewicht eine Magnetisierung einstellt die nicht vom Ort abhängt.

_Beachte_: Der tatsächliche Wert von $m$ der sich einstellt ist a priori unbekannt! Man kann nun weiterrechnen und kommt zu einer Gleichung für $m$

$$ m = \frac{1}{N}\sum_i s_i \frac{\exp(-\beta H_{MF})}{Z} = \tanh\left(\beta(Jzm-h)\right)$$

die (nährungsweise) nach $m$ gelöst werden muss, um die sich einstellende Magnetisierung zu erhalten.


```python
import sympy as sy
from sympy.utilities.codegen import codegen

x, a, b, c = sy.symbols("x a b c")

f = sy.Matrix([[x, a], [b, c]])

```

```python
args = (x, a, b, c)
func = sy.lambdify(args, f, "numpy")
```

```python
func(0, 1, 2, 3)
```

    array([[0, 1],
           [2, 3]])

```python
[(c_name, c_code), (h_name, c_header)] = codegen(
    name_expr=("diff", f),
    language="C",
    project="multi_pendulum",
    to_files=False
)
```

```python
print( c_name )
print( c_code )
print( h_name )
print( c_header )

f = open(c_name, 'w')
f.write(c_code)
f.close()

f = open(h_name, 'w')
f.write(c_header)
f.close()
```

    diff.c
    /******************************************************************************
     *                       Code generated with sympy 1.8                        *
     *                                                                            *
     *              See http://www.sympy.org/ for more information.               *
     *                                                                            *
     *                   This file is part of 'multi_pendulum'                    *
     ******************************************************************************/
    #include "diff.h"
    #include <math.h>

    void diff(double a, double b, double c, double x, double *out_8456883115516561320) {

       out_8456883115516561320[0] = x;
       out_8456883115516561320[1] = a;
       out_8456883115516561320[2] = b;
       out_8456883115516561320[3] = c;

    }

    diff.h
    /******************************************************************************
     *                       Code generated with sympy 1.8                        *
     *                                                                            *
     *              See http://www.sympy.org/ for more information.               *
     *                                                                            *
     *                   This file is part of 'multi_pendulum'                    *
     ******************************************************************************/

    #ifndef MULTI_PENDULUM__DIFF__H
    #define MULTI_PENDULUM__DIFF__H

    void diff(double a, double b, double c, double x, double *out_8456883115516561320);

    #endif


# From Newton to Schrödinger

## 🥅 Learning Objectives
- To learn about the historical origins of quantum mechanics.
- To introduce wave-particle duality and key concepts like the
  - Planck-Einstein relation for the energy of a photon
  - Einstein-Compton-De Broglie relation between momentum and wavelength
  - quantization of energy levels
- To introduce the Schrödinger equation and the probabilistic interpretation thereof.

## What is Quantum Mechanics
Starting in the late 1800’s, experimental physicists began to make observations that challenged the “classical” equations of physics.  What emerged was quantum mechanics. Quantum mechanics unifies and supersedes the theories of classical Newtonian mechanics and classical Maxwellian electromagnetism.

In classical physics, radiation and matter are inherently different:  radiation and matter are governed by different physical laws and modeled with different equations.  Quantum mechanics abolishes the inherent difference between radiation and matter.  Radiation—e.g., light—has properties (a well-defined energy and momentum) that are associated with matter.  Matter—e.g., an electron—has properties (a wavelength) that are associated with radiation.

The wave-like-ness of matter immediately leads to something called quantization. Matter is “chunky.”  There is no such thing as “half an electron.”  One always has an integer number of elementary particles.  So if I give you a system containing some number of electrons, you can safely say that the total mass of the system is

$$ m_{\text{total}} = n_e m_e $$

where $m_e$ is the mass of the electron and $n_e$ is a nonnegative integer. Using mass-energy equivalence, this means that the energy of these electrons should also be discrete

$$ E_{\text{total}} = n_e m_e c^2 $$

where $c$ is the speed of light.

Waves, however do not seem to be "chunky". If I want to put more energy into a wave, I can increase its amplitude or increase its frequency (by an noninteger amount).  So it would seem possible to make waves with any amount of energy.  It was Max Planck, in 1900, that proposed that there should also be a “quantization” of radiation, and that radiation should come in integer chunks, with the energy proportional to the frequency of the radiation:

$$ E_{\text{total}} \propto n_{ph} \nu_{ph} $$

The proportional constant is called *Planck's constant*:

$$ E_{\text{total}} = h n_{ph} \nu_{ph} $$

where

$$
\begin{align}
h &= 6.626 \cdot 10^{-34} \; \text{J} \cdot \text{s} \\\\
  &= 6.626 \cdot 10^{-34} \; \tfrac{\text{kg} \cdot \text{m}^2}{\text{s}} \\\\
  &= 6.626 \cdot 10^{-34} \; \tfrac{\text{V} \cdot \text{s}}{\text{C}}
\end{align}
$$

Planck's constant is very small, which explains why electromagnetic radiation does not seem to be "chunky" in everyday life. Similarly, the masses and charges of elementary particles are very small, which is why Newtonian mechanics is accurate enough to describe macroscopic phenomena. However, it is essential to include quantum effects when we deal with particles with very little mass (e.g., electrons).

Planck proposed the quantization of radiation as a mathematical trick to provide a model for blackbody radiation.  In 1905, Einstein interpreted this trick in the most literal possible way, proposing the quantization of light.  This interpretation led to an interpretation of the photoelectric effect, which eventually earned Einstein the Nobel Prize in Physics.  Planck also won the Nobel Prize in physics.

## How was Quantum Mechanics Discovered?
It is impossible to fully explain the historical foundations of quantum mechanics in a course like this one.  Instead, I will try to give you a very brief overview of some key experiments that changed physics.

### [Black-body Radiation](https://en.wikipedia.org/wiki/Black-body_radiation)

An inflammable object that absorbs all incident light, but does not transmit or reflect light, is called a black body. Because it is totally opaque and absorbs all light that falls upon it, a black body at zero Kelvin is the very definition of black. However, when you heat a blackbody, it glows. A good (but not perfect) example of a black body is a red-hot poker. An even better example is the sun.  Blackbody radiation has been observed by humans for millennia (or even longer): lava flows, molten glass, molten metal, and many other materials can be considered blackbodies. So it was extremely embarrassing to 19th-century physicists that they could not predict the spectrum—that is, which wavelengths of light occur with what intensity—of a blackbody.

In principle the spectrum of a black body should be easy to describe with classical statistical mechanics. However, classical statistical mechanics predicted that most of the light emitted would have a very short wavelength, the so-called "ultraviolet catastrophe." Planck suggested that the vibrations of atoms in a black body, and ergo the light emitted therefrom, was quantized into chunks. Planck understood that his result was significant, but he still thought of it as a mathematical trick.

### [Photoelectric Effect](https://en.wikipedia.org/wiki/Photoelectric_effect)

When one shines light on a metal surface, one observes that if the light has a low enough frequency (long enough wavelength), then no electrons escape from the surface. This holds (to an excellent approximation) no matter how intense the light is. However, with a sufficiently high frequency (short enough wavelength), electrons will start to escape from the surface. Importantly, the maximum kinetic energy of the electrons escaping from the surface grows as the frequency of the light increases, and the number of electrons escaping from the surface grows as the intensity of the light increases. But increasing the intensity of the light does not increase the maximum kinetic energy of the emitted electrons, nor does increasing the frequency of the light increase the number of electrons emitted.

Einstein decided to take Planck's mathematical expression at face value, and interpret light as coming in chunks called photons, with energy given by Planck's expression:

$$ E_{ph} = h \nu_{ph} $$

This allowed him to describe the photoelectric effect. Specifically, whenever $h \nu $ was less than a threshold value for the substance called its work function, $W$, no light would be emitted. (The work function can be thought of as the "ionization potential" for a metal.) But when $h \nu > W$, then photons had enough energy to remove an electron from the metal, and the surplus energy from the photon could be imparted to the electron as kinetic energy. Ergo

$$ \text{Kinetic Energy}_{\text{max}} = h \nu - W $$

As the light becomes more intense, the number of photons increases and the number of electrons emitted increases but, unless the light is very, very, very intense, the maximum kinetic energy of the electrons is left unaltered.

### [Compton Effect](https://en.wikipedia.org/wiki/Compton_scattering)

When you shine light on an (isolated) electron sitting at rest, sometimes the light scatters inelastically—that is the emitted light has a lower frequency than the incident light. The change in energy between the incident and emitted light is manifest in the kinetic energy of the electron. However, it's observed that the electron does not move in an arbitrary direction, but instead moves in the direction that would be expected if a photon were a classical particle moving at the speed of light with effective mass $m_{ph,\text{eff}} = \tfrac{h \nu}{c^2}$. Note that the photon does not actually have mass. However, the photon does have momentum, which explains the inelastic scattering result. The formula is derived by *assuming* that one can set the rest-mass-energy expression, $E = m c^2$, to the Planck relation for the energy of a photon. Then

$$
\begin{align}
m_{ph,\text{eff}} c^2 &= h \nu \\\\
m_{ph,\text{eff}} &= \tfrac{h \nu}{c^2}
\end{align}
$$

and the momentum of the photon would be its effective mass times its speed, $c$:

$$ p_{ph} = \tfrac{h \nu}{c} $$

Recall that the wavelength, $\lambda$, and frequency, $\nu$ of light are related according to the relation

$$ \nu = \tfrac{c}{\lambda} $$

This means that the momentum of light can be expressed as

$$ p_{ph} = \tfrac{h}{\lambda} $$

This is called the [De Broglie relation](https://en.wikipedia.org/wiki/Matter_wave). One of the key results of quantum mechanics is that the De Broglie relation holds for both waves (photons) and particles.

### [Davisson-Germer Experiment](https://en.wikipedia.org/wiki/Davisson%E2%80%93Germer_experiment)

Based on the results of Compton and De Broglie, one should expect that a beam of electrons should diffract, with patterns of constructive and deconstructive interference, according to their wavelength. The Davisson-Germer experiment confirmed this, showing that electrons (particles) can constructively and deconstructively interfere like waves.

### [Rydberg Formula](https://en.wikipedia.org/wiki/Rydberg_formula)

By the late 1800s, it was known that when an dilute atomic gas was heated, it did not emit as a black body, but only at certain characteristic wavelengths. Rydberg managed to describe the specific wavelengths of the emission by the empirical formula

$$
\frac{1}{\lambda} \propto \frac{1}{n^2} - \frac{1}{m^2}
$$

where $n$ and $m$ are positive integers: $n,m = 1,2,3,4,\ldots$. Recalling the link between frequency and wavelength, $\nu = \tfrac{c}{\lambda}$ and the Planck-Einstein relation for the energy of a photon,

$$
h \nu \propto \frac{1}{n^2} - \frac{1}{m^2}
$$

This suggests that photons are associated with transitions between energy levels that are labelled by indices, suggesting that the electronic energy levels of atoms are not arbitrary, but only have certain, quantized, values. The Rydberg formula is accurate for all one-electron atoms (at least until relativistic effects become nonneglible). It is also valid for highly-excited states (so-called Rydberg excitations) of many-electron atoms, where a single highly-excited electron is much further from the nucleus than the other electrons.

### Wave-Particle Duality

> It seems as though we must use sometimes the one theory and sometimes the other, while at times we may use either. We are faced with a new kind of difficulty. We have two contradictory pictures of reality; separately neither of them fully explains the phenomena ... but together they do.
    - Albert Einstein

The previous experiments, among many others, established that particles like electrons have wave-like properties, that electromagnetic radiation is composed of discrete photons with particle-like momentum and energy. The fact particles possess wave-like properties and waves provide particle-like properties is called [wave-particle duality](https://en.wikipedia.org/wiki/Wave%E2%80%93particle_duality): there is only one type of "stuff" in the universe and that stuff is "chunky" (like particles) and can constructively and deconstructively interfere (like waves). Moreover, the energies of atoms and molecules are quantized, having only specific discrete values.

#### 📝 Exercise: Suppose you are given a photon with an energy of 2 eV. What is its momentum in $\frac{\text{m} \cdot \text{kg}}{\text{s}}$? What is its frequency in Hz?
[Answer](IntroExercise1.ipynb)

#### 📝 Exercise: What is the momentum and energy of a photon with angular wavenumber $k=\frac{2\pi}{\lambda} = 10^7 \text{m}^{-1}$? Give your answer in SI units.
[Answer](IntroExercise2.ipynb)

## The Schrödinger Equation

It seems that, in physics, the more we learn, the simpler things become. We used to believe that there were two types of things, "particles" (matter) and "waves" (radiation). Now we understand that there is really only one type of entity, perhaps call it a *wavicle* if you must give it a name. Electromagnetic radiation comes in discrete chunks (photons) just like particles (e.g., electrons). Photons and particles have position(ish), momentum(ish), and energy(ish), just like classical particles of matter; both also can manifest constructive and deconstructive interference, just like classical waves. The difference between matter and radiation is merley one of perspective.

In 1925-26, Heisenberg and Schrödinger, working independently, built mathematical frameworks to describe the wave-particle duality of quantum particles. Nowadays we usually (except sometimes in spectroscopy) prefer Schrödinger's formulation. We will discuss the Schrödinger equation in more detail in the next unit, but for now it is worth merely comparing it to other equations from classical mechanics and classical electromagnetism, just to intuitively show how it encapsulates wave-particle duality. Of course, a [more rigorous justification for the Schrödinger equation](http://en.wikipedia.org/wiki/Theoretical_and_experimental_justification_for_the_Schr%C3%B6dinger_equation) is beyond the scope of this course.

For simplicity, we will only consider systems in one dimension.
> [**Wave Equation**](https://en.wikipedia.org/wiki/Wave_equation) (classical electromagnetism)
>
>$$
\frac{d^2u(x,t)}{dt^2} = c^2 \frac{d^2u(x,t)}{dx^2}
$$

> [**Hamilton-Jacobi Equation**](https://en.wikipedia.org/wiki/Hamilton%E2%80%93Jacobi_equation) (classical mechanics)
>
>$$
-\frac{dS(x,t)}{dt} = H
$$
> where $H = T + V$ is the sum of the kinetic energy, $T$, and the potential energy, $V$, written a function of $S$, $x$, and $t$. In the simplest case, one has:
>$$
-\frac{dS(x,t)}{dt} = \left( \frac{1}{2m} \frac{dS(x,t)}{dx} \cdot \frac{dS(x,t)}{dx} + V(x,t) \right)
$$
> because the momentum can be identified as $p(x,t) = \tfrac{dS(x,t)}{dx} $.

> [**Schrödinger Equation**](https://en.wikipedia.org/wiki/Schr%C3%B6dinger_equation) (quantum mechanics)
>
>$$ \frac{i h}{2 \pi} \frac{d \Psi(x,t)}{dt} =  \left( -\frac{h^2}{2m (2 \pi)^2 } \frac{d^2 \Psi(x,t)}{dx^2} + V(x,t)\Psi(x,t) \right) $$
>The solution, $\Psi(x,t)$ to the Schrödinger equation is called the [*wavefunction*](https://en.wikipedia.org/wiki/Wave_function). A key postulate of quantum mechanics is that the wavefunction contains all the information needed to fully specify the observable properties of a quantum system.

All three equations have strong resemblance. But, in particular, the Schrödinger equation and the Hamilton-Jacobi equation are consistent with each other. Suppose one uses the wavefunction ansatz,

$$ \Psi(x,t) = \psi_0(x,t) e^{\frac{2 \pi i S(x,t)}{h}} $$

where $\psi_0(x,t)$ and $S(x,t)$ are, without loss of generality, assumed to be real-valued functions. We will insert this expression into the Schrödinger equation, but first it is useful to define the quantity

$$
\hbar = \tfrac{h}{2 \pi}
$$

One then can rewrite the wavefunction and the Schrödinger equation without the pesky factors of $2 \pi$, respectively,

$$ \Psi(x,t) = \psi_0(x,t) e^{\frac{i S(x,t)}{\hbar}} $$

$$ i \hbar \frac{d \Psi(x,t)}{dt} =  \left( -\frac{\hbar^2}{2m} \frac{d^2 \Psi(x,t)}{dx^2} + V(x,t)\Psi(x,t) \right) $$
Inserting the wavefunction into the Schrödinger equation, the left-hand-side is

$$
 i \hbar \frac{d \Psi(x,t)}{dt} =e^{\frac{i S(x,t)}{\hbar}} \left( i \hbar \frac{d \psi_0(x,t)}{dt}-\psi_0(x,t) \frac{d S(x,t)}{dt} \right)
$$

and the right-hand-side is

$$
\begin{align}
& \left( -\frac{\hbar^2}{2m} \frac{d^2 \Psi(x,t)}{dx^2} +V(x,t)\Psi(x,t) \right) \\\\
& = e^{\frac{i S(x,t)}{\hbar}} \left(-\frac{\hbar^2}{2m} \frac{d^2 \psi_0(x,t)}{dx^2} - \frac{i \hbar}{m}\frac{d \psi_0(x,t)}{dx}\frac{d S(x,t)}{dx} - \frac{i \hbar}{2m} \psi_0(x,t) \frac{d^2 S(x,t)}{dx^2} + \frac{1}{2m}\psi_0(x,t)\left(\frac{d S(x,t)}{dx}\right)^2 + V(x,t) \psi_0(x,t) \right)
\end{align}
$$

Inserting these equations into the Schrödinger equation and dividing through by $e^{\frac{i S(x,t)}{\hbar}}$ gives:

$$
\begin{align}
& \left( i \hbar \frac{d \psi_0(x,t)}{dt}-\psi_0(x,t) \frac{d S(x,t)}{dt} \right) \\\\
& = \left(-\frac{\hbar^2}{2m} \frac{d^2 \psi_0(x,t)}{dx^2} - \frac{i \hbar}{m}\frac{d \psi_0(x,t)}{dx}\frac{d S(x,t)}{dx} - \frac{i \hbar}{2m} \psi_0(x,t) \frac{d^2 S(x,t)}{dx^2} + \frac{1}{2m}\psi_0(x,t)\left(\frac{d S(x,t)}{dx}\right)^2 + V(x,t) \psi_0(x,t) \right)
\end{align}
$$

The real and imaginary parts of this equation must be separately equal to each other. The real part of the equation is

$$
\begin{align}
-\psi_0(x,t) \frac{d S(x,t)}{dt}
= \left(-\frac{\hbar^2}{2m} \frac{d^2 \psi_0(x,t)}{dx^2} + \frac{1}{2m}\psi_0(x,t)\left(\frac{d S(x,t)}{dx}\right)^2 + V(x,t) \psi_0(x,t) \right)
\end{align}
$$

Dividing both sides by $\psi_0(x,t)$ and taking the classical limit, where $\hbar \rightarrow 0$, gives back the Hamilton-Jacobi equation,

$$ - \frac{d S(x,t)}{dt}
= \left(\frac{1}{2m}\left(\frac{d S(x,t)}{dx}\right)^2 + V(x,t) \right) $$

The imaginary part of the equation is:

$$ \hbar \frac{d \psi_0(x,t)}{dt}
= - \left(\frac{\hbar}{m}\frac{d \psi_0(x,t)}{dx}\frac{d S(x,t)}{dx} + \frac{\hbar}{2m} \psi_0(x,t) \frac{d^2 S(x,t)}{dx^2} \right) $$

We notice that this expression is zero in the classical limit. Dividing through by $\hbar$ and using the chain rule, $\frac{d \left(\psi_0(x,t)\right)^2}{dt} = 2 \psi_0(x,t) \frac{d \psi_0(x,t)}{dt}$, we can rewrite this expression as

$$ \frac{d \left(\psi_0(x,t)\right)^2}{dt}
= - \frac{d}{dx} \left(\psi_0(x,t)\right)^2 \left(\frac{1}{m}\frac{d S(x,t)}{dx}\right) $$

This expression resembles the [*equation of continuity*](https://en.wikipedia.org/wiki/Continuity_equation) from classical physics,

$$ \frac{d \rho(x,t)}{dt} = -\nabla \cdot \rho(x,t) \nabla v(x,t) $$

where $v(x,t)$ is the velocity and $\rho(x,t)$ is the density, the probability of observing a particle at the point $x$ and the time $t$. This (re)confirms that $\nabla S$ is an expression for the momentum, and suggests that

$$\rho(x,t) = \psi_0(x,t)^2 = \Psi(x,t) \Psi(x,t)^* = |\Psi(x,t)|^2 $$

is an expression for the probability of observing a particle at a point in space. The identification of the square-magnitude of the quantum-mechanical wavefunction with probability is called the [*Born postulate*](https://en.wikipedia.org/wiki/Born_rule).

In the end, the Schrödinger equation was not accepted because of its mathematical elegance, but because it worked to accurately describe key phenomena. In fact, as we shall see, the (originally empirical) Rydberg formula can be easily justified using the Schrödinger equation.

We can rationalize the form of the quantum mechanical momentum operator, $\hat{p} = -i \hbar \frac{d}{dx} $
, by a similar argument. Applying the hypothesized momentum operator to the wavefunction, one obtains:

$$
\begin{align}
-i \hbar \frac{d\Psi(x,t)}{dx} &= -i \hbar \frac{d\psi_0(x,t)}{dx} e^{\frac{i S(x,t)}{\hbar}} + \psi_0(x,t) e^{\frac{i S(x,t)}{\hbar}} \frac{dS(x,t)}{dx} \\\\
&= \text{[imaginary part]} + \frac{dS(x,t)}{dx} \Psi(x,t) \\\\
&= \text{[imaginary part]} + \text{[classical expression for momentum]}\Psi(x,t)
\end{align}
$$

This indicates that the quantum-mechanical momentum operator is consistent with the classical expression for the momentum, to which it reduces in the classical limit where $\hbar \rightarrow 0$.

## 🪞 Self-Reflection
Imagine you were assigned to describe Quantum Mechanics to a room full of irrascible tweens.
- How would you motivate them to be interested in quantum mechanics?
- How would you describe wave-particle duality in a way they could understand?

## ❓ Knowledge Test
- This [set of questions](IntroTestExample.ipynb) demonstrates the different types of knowledge-test questions that appear in the course, and how to solve them. [GitHub Classroom Link](https://classroom.github.com/a/d3E1lA4j)
- More questions, mostly about wave-particle duality. [GitHub Classroom Link](https://classroom.github.com/a/DMg3v70o)

## 🤔 Thought-Provoking Questions
- Is it possible for the wavelength of the scattered photon in Compton Scattering to be infinite? (I.e., could the photon be entirely absorbed?)
- What are the units of the wavefunction?

## 🔁 Recapitulation
- What are the following phenomena/experiments/theories, and why were they important to the establishment of the quantum theory?
  - black-body radiation and the ultraviolet catastrophe
  - the photoelectric effect
  - Compton scattering
  - the Davisson-Germer experiment (and electron scattering/diffraction in general)
  - the spectral lines of one-electron atoms, and the Rydberg relation
- What are the following key equations, and why are they important?
  - Planck-Einstein relation for the energy of a photon
  - De Broglie relation between momentum and wavelength
  - the Schrödinger equation
- What is the quantum mechanical operator for the momentum of a particle?
- List as many properties/characteristics of the wavefunction as you can.

## 🔮 Next Up...
- Motivate the time-dependent Schrödinger equation
- Derive the time-independent Schrödinger equation
- Explore the quantum-mechanical operator for momentum
- Study simple one-dimensional Hamiltonians

## 📚 References
My favorite sources for this material are:
- R. Eisberg and R. Resnick, Quantum Physics of Atoms, Molecules, Solids, Nuclei, and Particles (Wiley, New York, 1974)
- R. Dumont, [An Emergent Reality, Part 2: Quantum Mechanics](https://github.com/PaulWAyers/IntroQChem/blob/main/documents/DumontBook.pdf) (Chapters 1 and 2).
- Also see my (pdf) class [notes](https://github.com/PaulWAyers/IntroQChem/blob/main/documents/IntroQM.pdf).
- The introductory material in [Davit Potoyan's](https://www.chem.iastate.edu/people/davit-potoyan) Jupyter-book course is very good. Roughly [chapters 1 and 2](https://dpotoyan.github.io/Chem324/intro.html) are especially relevant here.

Some videos:
- [My video introduction](https://www.macvideo.ca/media/1_IntroQM/1_rbe31g5b)
- [History of Black-body radiation](https://www.youtube.com/watch?v=uMUi3o78qgQ&t=8s)
- [History of the Photoelectric effect](https://www.youtube.com/watch?v=BiPEY99w8Lo)

There are also some excellent wikipedia articles:
- On General Quantum Mechanics
  - [Introduction to Quantum Mechanics](http://en.wikipedia.org/wiki/Introduction_to_quantum_mechanics)
  - [Quantum Mechanics](http://en.wikipedia.org/wiki/Quantum_mechanics)
  - [Mathematical Formulation of Quantum Mechanics](http://en.wikipedia.org/wiki/Mathematical_formulation_of_quantum_mechanics)
  - [Basic Concepts of Quantum Mechanics](http://en.wikipedia.org/wiki/Basic_concepts_of_quantum_mechanics). See [also](https://github.com/PaulWAyers/IntroQChem/blob/main/documents/quantum_summary.pdf)
  - [Planck's Constant](http://en.wikipedia.org/wiki/Planck_constant)
  - [Wave-particle Duality](http://en.wikipedia.org/wiki/Wave-particle_duality)
- Black-Body Radiation
  - [What is a Black Body?](http://en.wikipedia.org/wiki/Black_body)
  - [Ultraviolet Catastrophe](http://en.wikipedia.org/wiki/Ultraviolet_catastrophe)
  - [Raleigh-Jeans Law for Classical Black Bodies](http://en.wikipedia.org/wiki/Rayleigh-Jeans_law)
  - [Planck's Law for Black-Body Radiation](http://en.wikipedia.org/wiki/Planck%27s_law)
  - [Wien's Displacement Law for the Peak Wavelength of a Black Body](http://en.wikipedia.org/wiki/Wien%27s_displacement_law)
- [Photoelectric Effect](http://en.wikipedia.org/wiki/Photoelectric_effect)
- [Compton Scattering](http://en.wikipedia.org/wiki/Compton_scattering)
- [De Broglie Wavelength](http://en.wikipedia.org/wiki/Matter_wave)
- Electron/Particle Diffraction
  - [Davisson-Germer Experiment](http://en.wikipedia.org/wiki/Davisson%E2%80%93Germer_experiment)
  - [Electron Diffraction](http://en.wikipedia.org/wiki/Electron_diffraction)
  - [Electron Crystallography](http://en.wikipedia.org/wiki/Electron_crystallography)
  - [Electron Microscope](http://en.wikipedia.org/wiki/Electron_microscopy)
  - [Transmission Electron Microscope](http://en.wikipedia.org/wiki/Transmission_Electron_Microscopy)
- Spectral lines in simple atoms
  - [Franck-Hertz Experiment, the first experiment to show discrete atomic energy levels](http://en.wikipedia.org/wiki/Franck%E2%80%93Hertz_experiment)
  - [Balmer formula, a predecessor of the Rydberg formula](http://en.wikipedia.org/wiki/Balmer_series)
  - [Rydberg formula](http://en.wikipedia.org/wiki/Rydberg_formula)
  - [Spectral Lines](http://en.wikipedia.org/wiki/Spectral_line)
  - [Atomic Spectral Lines](http://en.wikipedia.org/wiki/Atomic_spectral_line)
- Schrödinger Equation
  - [Schrödinger Equation](http://en.wikipedia.org/wiki/Schrodinger_equation)
  - [Theoretical Justification of S.E.](http://en.wikipedia.org/wiki/Theoretical_and_experimental_justification_for_the_Schr%C3%B6dinger_equation). See [also](https://github.com/PaulWAyers/IntroQChem/blob/main/linkedFiles/SchrodingerHJeq.pdf).
  - [Momentum Operator](http://en.wikipedia.org/wiki/Momentum_operator)


```python
from sympy import *
from sympy.abc import m,M,l,b,c,g,t
from sympy.physics.mechanics import dynamicsymbols, init_vprinting
th = dynamicsymbols('theta')
x = dynamicsymbols('x')
dth = diff(th)
dx = diff(x)
ddth = diff(dth)
ddx = diff(dx)
init_vprinting()
```

```python

```

```python
ddth = (-(1/2)*m*l cos(th)ddth - b*dx +(1/2)*m*l*sin(th)*dth*dx)/((m/12)(3l+l^2))
ddx  = (-(1/2)*m*l cos(th)ddth - b*dx +(1/2)*m*l*sin(th)*dth^2)/(M+m)
```


<p align="center">

</p>

## Interactive Bayesian Coin Demonstration from Sivia (1996)

### Sivia, D.S., 1996, Data Analysis: A Bayesian Tutorial

* interactive plot demonstration with ipywidget package

#### Michael Pyrcz, Associate Professor, University of Texas at Austin

##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)

#### The Bayesian Coin Example

I have a coin and you need to figure out if it is a fair coin!

* a fair coin would have a 50% probability of heads (and a 50% probability of tails)

You start with your prior assessment of my coin, a prior distribution over the probability of heads $Prob(Coin)$

* it could be based on how honest you think I am

Then you perform a set of coin tosses to build a likelihood distribution, $P(Tosses | Coin)$

* the more coin tosses, the narrower this distribution

Then you update the prior distribution with the likelihood distribution to get the posterior distribution, $P(Coin | Tosses)$.

\begin{equation}
P( Coin | Tosses ) = \frac{P( Tosses | Coin ) P( Coin )}{P( Tosses )}
\end{equation}

#### Objective

Provide an example and demonstration for:

1. interactive plotting in Jupyter Notebooks with Python packages matplotlib and ipywidgets
2. provide an intuitive hands-on example of Bayesian updating

#### Getting Started

Here's the steps to get setup in Python with the GeostatsPy package:

1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/).
2. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality.

#### Load the Required Libraries

The following code loads the required libraries.

```python
%matplotlib inline
from ipywidgets import interactive                        # widgets and interactivity
from ipywidgets import widgets                            # widgets and interactivity
import matplotlib; import matplotlib.pyplot as plt        # plotting
from matplotlib.ticker import (MultipleLocator, AutoMinorLocator) # control of axes ticks
plt.rc('axes', axisbelow=True)                  # set axes and grids in the background for all plots
import numpy as np                                        # working with arrays
from scipy.stats import triang                            # parametric distributions
from scipy.stats import binom
from scipy.stats import norm
```

#### Make Our Interactive Plot

For this demonstration we will:

* declare a set of 4 widgets in a HBox (horizontal box of widgets).

* define a function 'f' that will read the output from these widgets and make a plot

You may have some flicker and lag.  I have not tried to optimize performance for this demonstration.

```python
# 4 slider bars for the model input
a = widgets.FloatSlider(min=0.0, max = 1.0, value = 0.5, description = 'coin bias',continuous_update=False)
d = widgets.FloatSlider(min=0.01, max = 1.0, value = 0.1, step = 0.01, description = 'coin uncert.',continuous_update=False)
b = widgets.FloatSlider(min = 0, max = 1.0, value = 0.5, description = 'prop. heads',continuous_update=False)
c = widgets.IntSlider(min = 5, max = 1000, value = 100, description = 'coin tosses',continuous_update=False)

ui = widgets.HBox([a,d,b,c],)

def f(a, b, c, d):                                        # function to make the plot
    heads = int(c * b)
    tails = c - heads

    x = np.linspace(0.0, 1.0, num=1000)

    prior = norm.pdf(x,loc = a, scale = d)
    prior = prior / np.sum(prior)

    plt.subplot(221)

    plt.plot(x, prior,color='red',alpha=0.8,linewidth=3)  # prior distribution of coin fairness
    plt.arrow(0.48,0.04,-0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Tails',[0.10,0.042],rotation=0.0)
    plt.arrow(0.52,0.04,0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Heads',[0.55,0.042],rotation=0.0)
    plt.annotate('Double Tailed Coin',[0.02,0.02],rotation=90.0)
    plt.annotate('Double Headed Coin',[0.97,0.02],rotation=90.0)
    plt.xlim(0.0,1.0)
    plt.xlabel('Coin Bias = P(Coin Heads)'); plt.ylabel('Density'); plt.title(r'$Prior$ $Distribution$ - What do you think of the coin before the tosses?')
    plt.ylim(0, 0.05)
    plt.axvline(x=0.5,color='grey',linestyle='--');
    plt.annotate('Fair Coin',[0.49,0.02],rotation=90.0)
    plt.grid()
    plt.gca().xaxis.grid(True, which='major',linewidth = 1.0); plt.gca().xaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().yaxis.grid(True, which='major',linewidth = 1.0); plt.gca().yaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().tick_params(which='major',length=7); plt.gca().tick_params(which='minor', length=4)
    plt.gca().xaxis.set_minor_locator(AutoMinorLocator()); plt.gca().yaxis.set_minor_locator(AutoMinorLocator()) # turn on minor ticks

    plt.subplot(222)                                      # results from the coin tosses
    plt.pie([heads, tails],labels = ['heads','tails'],radius = 0.5*(c/1000)+0.5, autopct='%1.1f%%', colors = ['#ff9999','#66b3ff'], explode = [.02,.02], wedgeprops = {"edgecolor":"k",'linewidth': 1}  )
    plt.title(str(c) + ' Coin Tosses')

    likelihood = binom.pmf(heads,c,x)
    likelihood = likelihood/np.sum(likelihood)

    plt.subplot(223)                                      # likelihood distribution given the coin tosses
    plt.plot(x, likelihood,color='red',alpha=0.8,linewidth=3)
    plt.arrow(0.48,0.04,-0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Tails',[0.10,0.042],rotation=0.0)
    plt.arrow(0.52,0.04,0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Heads',[0.55,0.042],rotation=0.0)
    plt.annotate('Double Tailed Coin',[0.02,0.02],rotation=90.0)
    plt.annotate('Double Headed Coin',[0.97,0.02],rotation=90.0)
    plt.xlim(0.0,1.0); plt.ylim(0, 0.05)
    plt.xlabel('P(Tosses | Coin Bias)'); plt.ylabel('Density'); plt.title(r'$Likelihood$ $Distribution$ - What do the coin tosses tell you about the coin?')
    plt.axvline(x=0.5,color='grey',linestyle='--');
    plt.annotate('Fair Coin',[0.49,0.02],rotation=90.0)
    plt.grid()
    plt.gca().xaxis.grid(True, which='major',linewidth = 1.0); plt.gca().xaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().yaxis.grid(True, which='major',linewidth = 1.0); plt.gca().yaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().tick_params(which='major',length=7); plt.gca().tick_params(which='minor', length=4)
    plt.gca().xaxis.set_minor_locator(AutoMinorLocator()); plt.gca().yaxis.set_minor_locator(AutoMinorLocator()) # turn on minor ticks

    post = prior * likelihood
    post = post / np.sum(post)

    plt.subplot(224)                                      # posterior distribution
    plt.plot(x, post,color='red',alpha=0.8,linewidth=3)
    plt.arrow(0.48,0.04,-0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Tails',[0.10,0.042],rotation=0.0)
    plt.arrow(0.52,0.04,0.4,0,linewidth=1,facecolor='grey',edgecolor='black',head_length=.03)
    plt.annotate('Increasingly Weighted Heads',[0.55,0.042],rotation=0.0)
    plt.annotate('Double Tailed Coin',[0.02,0.02],rotation=90.0)
    plt.annotate('Double Headed Coin',[0.97,0.02],rotation=90.0)
    plt.xlim(0.0,1.0); plt.ylim(0, 0.05)
    plt.xlabel('P(Coin Bias | Tosses)'); plt.ylabel('Density'); plt.title(r"$Posterior$ $Distribution$ - What do you think of the coin after the tosses?")
    plt.axvline(x=0.5,color='grey',linestyle='--');
    plt.annotate('Fair Coin',[0.49,0.02],rotation=90.0)
    plt.grid()
    plt.gca().xaxis.grid(True, which='major',linewidth = 1.0); plt.gca().xaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().yaxis.grid(True, which='major',linewidth = 1.0); plt.gca().yaxis.grid(True, which='minor',linewidth = 0.2) # add y grids
    plt.gca().tick_params(which='major',length=7); plt.gca().tick_params(which='minor', length=4)
    plt.gca().xaxis.set_minor_locator(AutoMinorLocator()); plt.gca().yaxis.set_minor_locator(AutoMinorLocator()) # turn on minor ticks

    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.6, wspace=0.2, hspace=0.3)
    plt.show()

interactive_plot = widgets.interactive_output(f, {'a': a, 'd': d, 'b': b, 'c': c})
interactive_plot.clear_output(wait = True)                # reduce flickering by delaying plot updating

```

### Bayesian Coin Example from Sivia, 1996, Data Analysis: A Bayesian Tutorial
* interactive plot demonstration with ipywidget package

#### Michael Pyrcz, Associate Professor, University of Texas at Austin

##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)

### The Problem

What is the PDF for the coin probability of heads, P(Coin Heads)?  Start with a prior model and update with coin tosses.

* **coin bias**: expectation for your prior distribution for probability of heads

* **coin uncert.**: standard deviation for your prior distribution for probability of heads

* **prop. heads**: proportion of heads in the coin toss experiment

* **coin tosses**: number of coin tosses in thecoin toss experiment

```python
display(ui, interactive_plot)                            # display the interactive plot
```

    HBox(children=(FloatSlider(value=0.5, continuous_update=False, description='coin bias', max=1.0), FloatSlider(…

    Output()

#### Comments

This was a simple demonstration of interactive plots in Jupyter Notebook Python with the ipywidgets and matplotlib packages.

I have many other demonstrations on data analytics and machine learning, e.g. on the basics of working with DataFrames, ndarrays, univariate statistics, plotting data, declustering, data transformations, trend modeling and many other workflows available at https://github.com/GeostatsGuy/PythonNumericalDemos and https://github.com/GeostatsGuy/GeostatsPy.

I hope this was helpful,

*Michael*

#### The Author:

### Michael Pyrcz, Associate Professor, University of Texas at Austin
*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*

With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development.

For more about Michael check out these links:

#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

#### Want to Work Together?

I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.

* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you!

* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!

* I can be reached at mpyrcz@austin.utexas.edu.

I'm always happy to discuss,

*Michael*

Michael Pyrcz, Ph.D., P.Eng. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin

#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

```python

```


### Formation d'Ingénieur en Génie Mécanique
#### Eléments de bureau d'études (code UE TN12 niveau L3)
---

* Rapide tutoriel sur les [Notebook Jupyter](../../Tuto-Perso-Jupyter/Tuto-Jupyter.md)

---

## TD - Stabilité simplifiée d'un véhicule

Hypothèse simplificatrice :
- Suspensions et pneus déformables non considérés => solide indéformable
- Irrégularité de l’asphalte non considéré => coefficients de frottement et d’adhérence constants : adhérence pneu/route = 0.7

Véhicule :
- Voie $V$ = 1350 mm
- Empattement $E$ = 2400mm
- Centre de gravité : hauteur $h$ = 800mm et $l$ = 1450mm
- Poids : $M$=970kg

```python
## Donnee vehicule :
M = 970  # Masse totale en [kg]
V = 1350 # Voie en [mm]
E = 2400 # Empattement [mm]
h = 800  # Hauteur CdG [mm]
l = 1450 # Centrage CdG [mm]
g = 9.81 # Gravité [m/s2]
f = 0.7

```

Modèlisation :
* voiture = solide indéformable, en contact avec le sol en $4$ points.
* Contact roue/sol -> liaisons ponctuelles avec frottement.

----
### 1) Etude statique :

#### a) Efforts de contact des pneus sur le sol :

* On isole la voiture
* Le bilan des actions mécaniques extérieures fait apparaitre :
    * On fait l'hypothèse que le contact pneu/sol est modélisé par une liaison appui ponctuel avec frottement (frottement "sec", loi de Coulomb). Appui ponctuel car la surface de contact pneu/route est petite en regard des dimensions de la voiture.

$$
\{\mathcal{T}_{\text{Sol}\to\text{Roue }i}\}:
\left\lbrace
\begin{array}{c}
\vec{\mathcal{R}}_i\\
\vec{0}
\end{array}
\right\rbrace_{M_i}
\quad
i=1\ldots4
\quad
\{\mathcal{T}_{\text{Poids}\to\text{Voiture}}\}:
\left\lbrace
\begin{array}{c}
M\vec{g}\\
\vec{0}
\end{array}
\right\rbrace_{G}
$$

Dans ce bilan chaque résultante $\vec{\mathcal{R}}_i$ s'écrit avec 3 composantes, inconnues ($X_i$, $Y_i$, $Z_i$):

$$\vec{\mathcal{R}}_i = X_i\vec{x} + Y_i\vec{y} + Z_i\vec{z}$$

   * Avec un indice de mobilité $m=0$, un seul ensemble cinématiquement équivalent (donc 6 équations) on a :

    $$h = 0 + 12 -6=6$$

    Système hyperstatique de degré 6 qu'on ne peut pas résoudre complétement sans des équations supplémentaires.

    On peut considérer 2 façons d'écrire des relations suppplémentaires :
     1. Une symétrie plane par rapport à $[\vec{x},\vec{z}]$. On aurait alors au niveau des actions de contact, en restant en 3D :

    $$ Z_1 = Z_4\quad Z_2=Z_3\quad X_1=X_4\quad X_2=X_3 \quad Y_1=-Y_4\quad Y_2=-Y_3$$

     ou en traitant le problème comme plan, dans le plan $[\vec{x},\vec{z}]$ :

    $$ Z_1 = Z_4\quad Z_2=Z_3\quad X_1=X_4\quad X_2=X_3 \quad Y_1=Y_2=Y_3=Y_4=0$$

     2. On se donne une loi de comportement de l'ensemble "chassis + suspension + pneus" mais on quitte l'hypothèse d'un solide indéformable postulée au départ.

* PFS de la voiture dans le plan $[\vec{x},\vec{z}]$, en considérant la symétrie par rapport à ce plan.

$$
\left\lbrace
\begin{array}{crcl}
X_1 & l-E  &        & X_1\\
  0 & 0 & \wedge & 0\\
Z_1 & -h   &        & Z_1
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_2 & l  &        & X_2\\
  0 & 0 & \wedge & 0\\
Z_2 & -h   &        & Z_2
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_3 & l  &        & X_3\\
  0 & 0 & \wedge & 0\\
Z_3 & -h   &        & Z_3
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_4 & l-E  &        & X_4\\
  0 & 0 & \wedge & 0\\
Z_4 & -h   &        & Z_4
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{cl}
  0 & 0\\
  0 & 0\\
-Mg & 0
\end{array}
\right\rbrace_G
=\lbrace 0 \rbrace
$$

Avec

$$ Z_1 = Z_4\quad Z_2=Z_3\quad X_1=X_4\quad X_2=X_3$$

On supposera que $ Y_1=Y_2=Y_3=Y_4=0$ car ces composantes sont "hors plan".
Ici on dénombre $12-4=8$ inconnues, et $3$ équations du PFS + $4$ équations venant de la symétrie, aucune mobilité donc :

$$h = 0 + 8 - (3+4) = 1$$

Le calcul donne :

$$ Z_1=Z_4=\frac{Mgl}{2E}$$

$$ Z_2=Z_3=\frac{Mg(E-l)}{2E}$$

$$ X_1=X_4$$

$$ X_2=X_3=-X_4$$

On ne peut pas déterminer les composantes horizontales des actions de contact, mais on remarquera que la solution $X_1=X_2=X_3=X_4=0$ est une solution du système.

```python
Z1 = M*g*l/(2*E)
Z2 = M*g*(E-l)/(2*E)
print('Sur chaque roue AV %.2f N'%Z2)
print('Sur chaque roue AR %.2f N'%Z1)
```

#### b) Effort tangentiel maximal en adhérence des roues avant et des roues arrière :

* Loi de Coulomb (frottement sec) à la limite d'adhérence, la composante normale au plan tangent commun des géométries en contact $\vec{N}$ et la composante tangentielle (dans le plan) $\vec{T}$ sont telles que :

$$\Vert\vec{T}\Vert=f\Vert\vec{N}\Vert$$

Avec $f=\tan\varphi$ le coefficient de frottement.

* Sur les roues du train AV :

$$X_2=X_3=fZ_2=fZ_3$$

* Sur les roues du traint AR :

$$X_1=X_4=fZ_1=fZ_4$$

```python
X1 = f*M*g*l/(2*E)
X2 = f*M*g*(E-l)/(2*E)
print('Sur chaque roue AV %.2f N'%X2)
print('Sur chaque roue AR %.2f N'%X1)
```

#### c) Accélération maximale que peut réaliser cette voiture avec roue AR motrice :

* La voiture accélére parce qu'une force "motrice" s'applique sur celle-ci et modifie son équilibre, i.e. la somme des torseurs actions mécaniques extérieures n'est plus nulle.
* En supposant que cette force "motrice" $\vec{F}_{Mot}$ s'applique au CdG de la voiture (ce qui n'est pas le cas en réalité, elle s'applique au niveau du contact roue motrice/sol) on peut écrire, dans le repère Galiléen lié au sol $\mathcal{R}_g$ :

$$\vec{F}_{Mot} = M\vec{a}_{\text{Voiture}/\mathcal{R}_g}$$

Ici la force motrice maximale donne l'accélération maxi et c'est la composante horizontale maximale transmissible par adhérence des actions de contact roue AR/sol, et donc :

$$a_{max} = \frac{2X_1}{M} = \frac{glf}{E}$$

```python
a = f*g*l/E
print ('acceleration maxi %.2f [m/s2] soit %.2fg '%(a,f*l/E))
```

On va voir que ce caclul ne permet pas d'obtenir la bonne valeur de l'accélération maximale car la force motrice ne s'applique pas au CdG.

---
### 2) Etude dynamique :

#### a) Accélération de 0.4g :

* Le PFD  appliqué à la voiture, dans le repère galiléen lié au Sol $\mathcal{R}_g$ s'écrit :

$$
\sum_{i=1}^{i=4}
\left\lbrace
\begin{array}{c}
\vec{\mathcal{R}}_i\\
\vec{GM_i}\wedge\vec{\mathcal{R}}_i
\end{array}
\right\rbrace_{G}
+
\left\lbrace
\begin{array}{c}
M\vec{g}\\
\vec{0}
\end{array}
\right\rbrace_{G}
=
\left\lbrace
\begin{array}{c}
M\vec{a}_{\text{Voiture}/\mathcal{R}_g}\\
\vec{0}
\end{array}
\right\rbrace_{G}
$$

Ici on suppose que la voiture est en mouvement rectiligne, et on néglige les inerties des parties tournantes (celles des roues, dans les élements de transmission et le moteur), donc en $G$ le moment dynamique de la voiture est nul.

En supposant le problème plan comme pour le PFS on obtient :

$$
\left\lbrace
\begin{array}{crcl}
X_1 & l-E  &        & X_1\\
  0 & 0 & \wedge & 0\\
Z_1 & -h   &        & Z_1
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_2 & l  &        & X_2\\
  0 & 0 & \wedge & 0\\
Z_2 & -h   &        & Z_2
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_3 & l  &        & X_3\\
  0 & 0 & \wedge & 0\\
Z_3 & -h   &        & Z_3
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
X_4 & l-E  &        & X_4\\
  0 & 0 & \wedge & 0\\
Z_4 & -h   &        & Z_4
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{cl}
  0 & 0\\
  0 & 0\\
-Mg & 0
\end{array}
\right\rbrace_G
=
\left\lbrace
\begin{array}{cl}
 Ma & 0\\
  0 & 0\\
  0 & 0
\end{array}
\right\rbrace_G
$$

On obtient les solutions :

$$Z_1=Z_4=\frac{M(ah+gl)}{2E}$$

$$Z_2=Z_3=\frac{M(g(E-l)-ah)}{2E}$$

$$X_1=X_4$$

$$X_2=X_3=\frac{Ma}{2}-X_4$$

Avec $a=0.4g$ on a :

```python
a = 0.4*g
Z1a = M*(a*h+g*l)/(2*E)
Z2a = M*(g*(E-l)-a*h)/(2*E)
print('Sur chaque roue AV %.2f N'%Z2a)
print('Sur chaque roue AR %.2f N'%Z1a)
print('Transfert de charge :')
```

Le "transfert de charge" en % est :

```python
print('Roue AV %+.2f%%'%(100*(Z2a-Z2)/Z2))
print('Roue AR %+.2f%%'%(100*(Z1a-Z1)/Z1))
```

    Roue AV -33.68%
    Roue AR +22.07%

----

*Remarques :*

Le sens des efforts indiqués sur la figure, ainsi que le sens du vecteur $M\vec{a}_{\text{Voiture}/\mathcal{R}_g}$ sont contraires à l'intuition ou les effets ressentis, la voiture lors de l'accélération à tendance à basculer vers l'arrière, sensation identique à celle du conducteur ....

Explication :

Le PFD sous la forme énoncée par Newton n'est vraie que dans un repère Galiléen (i.e tout repère dans lequel, soumis à aucune action mécanique extérieure, le solide a un mouvement rectiligne à vitesse constante).
Lorsqu'on veut écrire le PFD dans un repère non Galiléen, il faut exprimer l'accélération dans un repère Galiléen (il en existe toujours un .. la terre, le soleil, des étoiles lontaines, etc .. cela dépend de la précision suohaitée dans la modélisation) avec la composition des accélérations (cf cours de cinématique de 1er cycle) pour un point $P$ quelconque du solide $S$ avec $\mathcal{R}_g$ repère Galiléen et $\mathcal{R}_1$ un repère quelconque :

$$
\vec{a}_{P\in S/\mathcal{R}_g} = \underbrace{\vec{a}_{P\in S/\mathcal{R}_1}}_\text{Acc. Relative} + \underbrace{\vec{a}_{P\in \mathcal{R}_1/\mathcal{R}_g}}_\text{Acc. d'Entrainement} + \underbrace{2\vec{\Omega}_{S/\mathcal{R}_1}\wedge \vec{V}_{P\in S/\mathcal{R}_1}}_\text{Acc. de Coriolis}
$$

Lorsqu'il n'y a pas de mouvement de rotation $\vec{\Omega}_{S/\mathcal{R}_1}=\vec{0}$ et en assimilant $S$ à la voiture de masse $M$, le PFD s'écrit dans $\mathcal{R}_g$ :

$$
\sum\vec{F}_{\text{Ext}\to\text{Voiture}} = M\vec{a}_{G\in\text{Voiture}/\mathcal{R}_g} =
M \left( \vec{a}_{G\in\text{Voiture}/\mathcal{R}_1} + \vec{a}_{G\in\mathcal{R}_1/\mathcal{R}_g}\right)
$$

Soit :

$$
\sum\vec{F}_{\text{Ext}\to\text{Voiture}} - M\vec{a}_{G\in\mathcal{R}_1/\mathcal{R}_g} = M\vec{a}_{G\in\text{Voiture}/\mathcal{R}_1}
$$

Cela ressemble donc au PFD de la voiture dans $\mathcal{R}_1$ mais avec le terme $- M\vec{a}_{G\in\mathcal{R}_1/\mathcal{R}_g}$ en plus du côté des actions mécaniques mécaniques. Ce terme a la dimension d'une force, mais n'est pas une force au sens de la mécanique Newtionnienne (i.e une interaction). Certains auteurs l'appelle "force d'inertie" ou "pseudo force d'inertie".

Dans notre exemple en considérant un repère $\mathcal{R}_1$ lié à la voiture on a évidemment :

$$\vec{a}_{G\in\text{Voiture}/\mathcal{R}_1}=\vec{0}$$

et

$$\vec{a}_{G\in\mathcal{R}_1/\mathcal{R}_g} = \vec{a}_{G\in\text{Voiture}/\mathcal{R}_g}$$
On retrouve alors du côté des actions mécaniques :

$$
-Mvec{a}_{G\in\mathcal{R}_1/\mathcal{R}_g} = -M\vec{a}_{G\in\text{Voiture}/\mathcal{R}_g}
$$

cette grandeur homogène à une force qui explique l'intuition et le ressenti du conducteur (qui sur le conducteur s'exprime avec la masse du conducteur, bien sur).

----

#### b) Accélération maximale à la limite d'adhérence

* En supposant que les roues AR sont motrices, à la limite d'adhérence on a :

$$X_{1,4}\leq fZ_{1,4}= f\frac{M(ah+gl)}{2E}\quad\text{et}\quad X_{2,3}=0$$

on déduit des équations du PFD :

$$X_1 = X_4 = \frac{Ma}{2}$$

et :

$$\frac{Ma}{2} \leq  f\frac{M(ah+gl)}{2E}$$

soit la valeur maximale de l'accélération :

$$a\leq a_{max}=\frac{fl}{E-hf}g$$

```python
amax = (f*l)/(E-h*f)*g
print ('acceleration maxi %.2f [m/s2] soit %.2fg '%(amax,amax/g))
```

    acceleration maxi 5.41 [m/s2] soit 0.55g

* En supposant que les roues AV sont motrices, à la limite d'adhérence on a :

$$X_{2,3}\leq fZ_{2,3}= f\frac{M(g(E-h)-ah)}{2E}\quad\text{et}\quad X_{1,4}=0$$

on déduit des équations du PFD :

$$X_2 = X_2 = \frac{Ma}{2}$$

et :

$$\frac{Ma}{2} \leq  f\frac{M(g(E-h)-ah)}{2E}$$

soit la valeur maximale de l'accélération :

$$a\leq a_{max}=\frac{f(E-l)}{E-hf}g$$

```python
amax = f*(E-l)/(E-h*f)*g
print ('acceleration maxi %.2f [m/s2] soit %.2fg '%(amax,amax/g))
```

    acceleration maxi 3.55 [m/s2] soit 0.36g

* En supposant que les roues AV et AR sont motrices, à la limite d'adhérence on a :

$$X_{2,3}\leq fZ_{2,3}= f\frac{M(g(E-h)-ah)}{2E}\quad\text{et}\quad X_{1,4}\leq fZ_{1,4}= f\frac{M(ah+gl)}{2E}$$

on déduit des équations du PFD :

$$X_1 + X_2 + X_3 + X_4 = Ma$$

et :

$$Ma \leq  f\left[\frac{M(g(E-l)-ah)}{E} + \frac{M(ah+gl)}{E}\right]$$

soit la valeur maximale de l'accélération :

$$a\leq a_{max}=fg$$

---
### 3) Etude de stabilité :

#### a) Bascule en roue arrière :

* La voiture commence à basculer lorsque la composante normale des actions de contact roue AV/sol deviennent nulles, et bascule lorsque celles-ci sont négatives donc :

$$Z_2=Z_3=\frac{M(g(E-l)-ah)}{2E}<0$$
L'accélération $a$ qui permet cette bascule doit être telle que :

$$a>g\frac{(E-l)}{h}$$
Avec les roues arrières motrice, l'accélération maximale à la limite de l'adhérence est telle que :

$$a_{max}=\frac{fl}{E-hf}g$$

pour qu'il y ait bascule on doit avoir :

$$a_{max}=\frac{fl}{E-hf}g>g\frac{(E-l)}{h}$$

soit

$$l>E-hf$$

```python
lbas = E - h*f
print('l pour bascule : %.2fmm'%lbas)
```

    l pour bascule : 1840.00mm

#### b) Bascule en virage :

* Pour cette étude il faut écrire le PFD de la voiture en virage, donc dans un mouvement de rotation autour d'un point fixe. Pour traitet cette étude simplement i.e dans le plan $[\vec{y},\vec{z}]$, il faut faire les hypothèses "fortes" suivantes :
    * Vitesse de giration constante $\to$ pas d'accélération tangentielle (suivant $\vec{x}$).
    * Symétrie des actions de contact AR/AV, cela suppose que CdG de la voiture est situé au centre.

Le PFD s'écrit dans le repère Galiléen $\mathcal{R}_g$ associé au sol :

$$
\sum\limits_{i=1}^{i=4}
\left\lbrace
\begin{array}{c}
\vec{\mathcal{R}}_i\\
\vec{GM_i}\wedge\vec{\mathcal{R}}_i
\end{array}
\right\rbrace_{G}
+
\left\lbrace
\begin{array}{c}
M\vec{g}\\
\vec{0}
\end{array}
\right\rbrace_{G}
=
\left\lbrace
\begin{array}{c}
M\vec{a}_{\text{Voiture}/\mathcal{R}_g}\\
\vec{0}
\end{array}
\right\rbrace_{G}
$$

Ici, avec $O$ sur l'axe de rotation , et [O,$\vec{x}_0,\vec{y}_0,\vec{z}_0]$ le repère fixe associé à $\mathcal{R}_g$

$$
\vec{a}_{\text{Voiture}/\mathcal{R}_g} = \frac{\mathrm{d}^2}{\mathrm{d}t^2}\left[\vec{OG}\right]=\frac{\mathrm{d}^2}{\mathrm{d}t^2}\left[R(\cos\theta\vec{y}_0 + \sin\theta\vec{x}_0)\right] = -R\dot{\theta}^2\vec{y}\quad\text{(avec $\ddot{\theta}=0$)}
$$

---

*Remarques :*

Ici on peut faire la même remarque avec le même raisonnement et ce que l'on nomme "force centrifuge" qui s'écrit : $MR\dot{\theta}^2\vec{y}$.

---

On a alors (en considérant $X_1=X_2=X_3=X_4=0$ pour être dans le plan plan $[\vec{y},\vec{z}]$) avec $Z_1=Z_2$ et $Z_3=Z_4$, $Y_1=Y_2$ et $Y_3=Y_4$ comme conditions de symétrie :

$$
\left\lbrace
\begin{array}{crcl}
0 & 0  &        & 0\\
Y_1  & -V/2 & \wedge & Y_1\\
Z_1 & -h   &        & Z_1
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
0 & 0  &        & 0\\
Y_2 & -V/2 & \wedge & Y_2\\
Z_2 & -h   &        & Z_2
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
  0 & 0  &        & 0\\
Y_3 & V/2 & \wedge & Y_3\\
Z_3 & -h   &        & Z_3
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{crcl}
  0 & 0  &        & 0\\
Y_4 & V/2 & \wedge & Y_4\\
Z_4 & -h   &        & Z_4
\end{array}
\right\rbrace_G
+
\left\lbrace
\begin{array}{cl}
  0 & 0\\
  0 & 0\\
-Mg & 0
\end{array}
\right\rbrace_G
=
\left\lbrace
\begin{array}{cl}
  0 & 0\\
  -MR\dot{\theta}^2 & 0\\
  0 & 0
\end{array}
\right\rbrace_G
$$

$$
\left\lbrace
\begin{array}{rl}
Y_1 + Y_2 + Y_3 + Y_3 &= -MR\dot{\theta}^2\\
Z_1 + Z_2 + Z_3 + Z_4 -Mg &= 0\\
\dfrac{V}{2}(-Z_1-Z_2+Z_3+Z_4) + h(Y_1+Y_2+Y_3+Y_4)&=0
\end{array}
\right.
$$

On obtient :

$$Y_1 = -Y_4-\frac{MR\dot{\theta}^2}{2}$$

$$Z_1 = M\left[\frac{g}{4}-\frac{h}{2V}R\dot{\theta}^2\right]\quad Z_4 = M\left[\frac{g}{4}+\frac{h}{2V}R\dot{\theta}^2\right]$$

Pour traiter la bascule en virage, ll faut considérer deux situations :
1. La voiture glisse vers l'extérieur du virage et ne bascule pas.
2. La voiture ne glisse pas mais bascule.

Dans le cas 1) la condition de glissement la plus sévère s'écrit sur la roue extérieure au virage car $Z_4>Z_1$ et on a adhérence tant que :

$$\vert Y_4\vert\leq f\vert Z_4\vert$$

Soit :

$$\vert Y_1-\frac{MR\dot{\theta}^2}{2} \vert \leq M\left[\frac{g}{4}+\frac{h}{2V}R\dot{\theta}^2\right]$$

Il faudrait connaître la valeur de $Y_1$, on peut supposer $Y_1\simeq\varepsilon\simeq0$, tous les efforts de contact sont sur les roues extérieures au virage ...

$$\frac{MR\dot{\theta}^2}{2}\leq M\left[\frac{g}{4}+\frac{h}{2V}R\dot{\theta}^2\right]$$

Soit

$$R\dot{\theta}^2\leq g\frac{Vf}{2(V-fh)}$$

```python
kg=(V*f)/(2*(V-f*h))
print('accélération normale maxi avant glissement %.3fg'%kg)
```

    accélération normale maxi avant glissement 0.598g

Dans le cas 2), la basucle se produit (en supposant qu'il n'y ait pas glissement !!) lorsque $Z_{1,2}<0$ donc :

$$\frac{g}{4}-\frac{h}{2V}R\dot{\theta}^2<0$$

Soit :

$$R\dot{\theta}^2>g\frac{V}{2h}$$

```python
kb=V/(2*h)
print('accélération normale maxi avant bascule %.3fg'%kb)
```

    accélération normale maxi avant bascule 0.844g

On voit que la voiture va glisser avant de basculer, ce qui est plutôt rassurant !!!

Pour que la voiture puisse basculer il faudrait que :

$$
g\frac{Vf}{2(V-fh)} \geq g\frac{V}{2h}\Longrightarrow \frac{f}{(V-fh)} \geq \frac{1}{h}\Longrightarrow (V-fh)\leq hf
$$

Soit :

$$
\frac{V}{h}\leq 2f
$$

Avec la même voie $V$, il faudrait augmenter la hauteur du CdG de $164$mm.

----
#### BONUS :

#### Résolution par calcul formel du PFS de la voiture :

On montre ici comment résoudre par calcul formel un PFS. Calcul formel = calcul littéral par oppositon à calcul numérique.

```python
from sympy import *                  # Libraire de calculs symboliquen docs->http://www.sympy.org
from sympy.vector import CoordSys3D  # Module de gestion des vecteurs de sympy.
from IPython.display import display, Latex
# On défini les variables symboliques qui seront utilisées
# dans les calculs.
e,V,l,h,M,g,a = symbols ('e V l h M g a')

# On défini les 12 composantes, inconnues des actions de contact :
# Roue ARG:
X1,Y1,Z1 = symbols ('X_1 Y_1 Z_1')
# Roue AVG:
X2,Y2,Z2 = symbols ('X_2 Y_2 Z_2')
# Roue AVD:
X3,Y3,Z3 = symbols ('X_3 Y_3 Z_3')
# Roue ARD:
X4,Y4,Z4 = symbols ('X_4 Y_4 Z_4')

# Repere :
Rg = CoordSys3D('Rg')
# Vecteur des positions des points de contact par
# rapport à G.
GM1 =  (l-e)*Rg.i  - V/2*Rg.j - h*Rg.k
GM4 =  (l-e)*Rg.i  + V/2*Rg.j - h*Rg.k
GM2 =      l*Rg.i  - V/2*Rg.j - h*Rg.k
GM3 =      l*Rg.i  + V/2*Rg.j - h*Rg.k

# On défini les résultantes des actions mécaniques :
R1 = X1*Rg.i + Y1*Rg.j + Z1*Rg.k
R2 = X2*Rg.i + Y2*Rg.j + Z2*Rg.k
R3 = X3*Rg.i + Y3*Rg.j + Z3*Rg.k
R4 = X4*Rg.i + Y4*Rg.j + Z4*Rg.k

# La résultant du PFS -> equation vectorielle
RPFS  = R1 + R2 + R3 + R4 - M*g*Rg.k

# Le moment résultant du PFS -> equation vectorielle
# On passe par les équations vectorielles pour calculer les moments automatiquement
# avec le produit vectoriel : .cross() -> une des métodes des objets vecteurs de sympy.

MPFS = GM1.cross(R1) + GM2.cross(R2) + GM3.cross(R3) + GM4.cross(R4)
```

```python
# Ici on calcule les équations scalaires du PFS
# en projetant les équations vectorielles sur les axes
# du repère -> on utilise le produit scalaire .dot()
PFS = []
for unit_vector in Rg.base_vectors():
    PFS.append(RPFS.dot(unit_vector))
for unit_vector in Rg.base_vectors():
    PFS.append(MPFS.dot(unit_vector))
```

```python
# On ajoute les équations de symétrie
# Par défaut l'équation est : Z1-Z4==0
PFS.append(Z1 - Z4)
PFS.append(X1 - X4)
PFS.append(Z2 - Z3)
PFS.append(X2 - X3)

# On ajoute les équations permettant de traiter ce problème dans le plan [X,Z] :
# Pour i=1..4, on ajoute des équations Yi=0.
PFS.append(Y1)
PFS.append(Y2)
PFS.append(Y3)
PFS.append(Y4)
```

```python
# On résout symboliquement le système de 12 équations et 12 inconnues :
Sol = solve(PFS,[X1,Y1,Z1,X2,Y2,Z2,X3,Y3,Z3,X4,Y4,Z4])
# Affichage des solutions :
display(Latex('$'+latex(Sol)+'$'))
```

$\left\{ X_{1} : - X_{3}, \  X_{2} : X_{3}, \  X_{4} : - X_{3}, \  Y_{1} : 0, \  Y_{2} : 0, \  Y_{3} : 0, \  Y_{4} : 0, \  Z_{1} : \frac{M g l}{2 e}, \  Z_{2} : \frac{M g \left(e - l\right)}{2 e}, \  Z_{3} : \frac{M g \left(e - l\right)}{2 e}, \  Z_{4} : \frac{M g l}{2 e}\right\}$