---
execution:
  timeout: 60
jupytext:
  cell_metadata_filter: -all
  formats: md:myst,ipynb
  main_language: python
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.16.7
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---
```{code-cell}
:tags: [hide-cell]

import mmf_setup; mmf_setup.nbinit()
import os, sys
from pathlib import Path
FIG_DIR = Path(mmf_setup.ROOT) / '../Docs/_build/figures/'
os.makedirs(FIG_DIR, exist_ok=True)
import logging; logging.getLogger("matplotlib").setLevel(logging.CRITICAL)
%matplotlib
import numpy as np, matplotlib.pyplot as plt
try: from myst_nb import glue
except: glue = None

from phys_581.contexts import FPS
```

<!-- Physics 581: Physics Inspired Computational Techniques documentation master file, created by
   sphinx-quickstart on Tue Aug 10 12:38:54 2021.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.
-->

:::{margin}
Some {ref}`sec:ProgrammingExamples`:

<video width="100%" mute autoplay loop controls>
<source type="video/mp4" src="_static/MD.mp4">
Your browser does not support the video tag.
</video>

*Molecular dynamics simulation of a projectile hitting a beam.*

:::
:::{margin}
<video width="100%" mute autoplay loop controls>
<source type="video/mp4" src="_static/SW.mp4">
Your browser does not support the video tag.
</video>

*Navier-Stokes simulation of a viscous fluid flowing over a bump in a
harmonic trap.*
:::

# Physics 581: Compute Anything -- <br/> Effective Scientific Computing

* **Who:** Graduate students or upper-level undergraduates from across the university
  with a strong preparation in mathematics (strong calculus and linear algebra with
  exposure to differential equations).  Programming experience would be helpful, but
  we will provide support those with weaker backgrounds.
* **When: MW(F)2-4pm**: Fridays are part   of the iSciMath Coffee Hours and will be for
  working on problems, discussions, etc.
<!-- * **Where: Webster 941**: Venue may change depending on interest. -->

Imagine designing a rocket: You need to minimize weight, but maintain strength.  Do you
need to start a million-dollar program to model structures, or can you use computer
modeling to help? What will get you to space fastest, **and** keep you in one piece?
**Compute Anything** will lower the barrier for computing, allowing you to quickly explore
problems as both a programmer and a scientist.  We will show you that programming can
**fun, dynamic, and interactive**, accelerating your research and your career. 

If you have any questions, please contact Michael McNeil Forbes
[m.forbes@wsu.edu](mailto:m.forbes+581@wsu.edu).

## FAQ

* **Q: What language are we using?**

  **A:** Python with the [NumPy][], [SciPy][], and [Matplotlib][] packages. 
  However, if you have experience with another language, we encourage
  **multilinguality**.
  
  :::{admonition} Why Python?
  :class: dropdown
  
  Python has many advantages: It is well designed, in high demand at labs and in
  industry, has excellent library support and a strong and supportive community.  It can
  be slow, but there are many opportunities for optimization and with these, is suitable
  for many demanding applications.  Matlab is a serious contender, but can be expensive
  and the language itself is not as well-designed as Python.  For high-performance code,
  Julia is a good option, as are Fortran and C/C++, but the latter are not as useful for
  quickly exploring.
  :::
  
* **Q: I've already taken a programming course. Why should I take this one?**

  **A:** This course will teach you much more than just programming.  We will teach you
  programming best-practices like version control and documentation for reproducibility, 
  and testing to ensure correctness -- techniques you can directly use to improve your
  research. By the end of the course, you will have an online programming portfolio,
  the skills to write a paper or thesis with publication quality figures, and know how
  to reliably and reproducibly explore data.
  
* **Q: What will class be like?**

  :::{margin}
  This is sometimes called a **minimial viable product** (MVP), and the technique is
  akin to using **tracer bullets** to make sure you don't run into any roadblocks after
  length coding effort.
  :::
  **A:** We will typically start with a brief lecture establishing the mathematical
  underpinning of a technique.  Then we will quickly implement simple examples together
  to ensure that things work as expected and develop **intuition**.  Once these basics
  are established we will explore simply problems, then try to break our code with
  edge-cases as we write tests. Later in the course, we will revisit and use our
  validated code to explore more complex problems, profiling and optimizing as needed
  for performance, or targeting our code for high-performance computing (HPC) clusters.

* **Q: Do I need to be an experience programmer?**

  **A:** While programming experience will help, we will provide support for those with
  limited programming experience to help you get up to speed.  If you are concerned,
  please reach out for some introductory materials to help you get started.

* **Q: I don't need any more courses.  Why should I take this class?**

  **A:** This class is aimed at improving your productivity.  If your research is well
  under way, then you are encouraged to bring a relevant project to the class, and will
  receive credit for completing a research-relevant programming project demonstrating
  the principles of pragmatic programming: i.e., a version-controlled repository of
  well-documented and well tested code aimed at helping your research, with a skeleton
  paper or thesis meeting all the technical and formatting requirements of a relevant
  journal or the graduate school.

For example, in addition to the technical {ref}`sec:LearningOutcomes` at the heart of
scientific computing, we will emphasize the following concepts:


* **Language:** At a basic level, we must be able to tell computers what we want them to
  do, but this is only one function of programming languages: they also **enable you to
  think** differently.  I thus strongly encourage you to become **multilingual**,
  especially learning languages from different [programming paradigms][].

  ::::::::::{admonition} Real Examples: Vectorization
  :class: dropdown

  Language fundamentally influences how we think[^Sapir–Whorf], and programming
  languages help me think about scientific problems.  For a concrete example, [MATLAB][]
  and [NumPy][] (in [Python][]) induced me to think about algorithbms in terms of
  [vectorization][array programming], working with arrays as a whole rather than
  worrying about components and for loops.  This is the programming analog of index-free
  notation in mathematics (another "language") for describing geometry and tensors.  

  Consider the following mathematical expressions for matrix multiplication and their
  equivalent representation in [Python][] (using [NumPy][]):

  <!-- For tab-set examples and formats, see:
       https://sphinx-design.readthedocs.io/en/latest/tabs.html -->
  ::::::{tab-set}
  :::::{tab-item} Loops

  \begin{gather*}
    C_{ml} = \sum_{m=0}^{M-1} A_{mn} B_{nl} 
  \end{gather*}

  ````{tab-set-code}
  ```python
  import numpy as np
  A = ...
  B = ...
  M, N = A.shape
  _, L = B.shape
  C = np.zeros((M, L))
  for m in range(M):
      for l in range(L):
         for n in range(N):
             C[m, l] += A[m, n] * B[n, l]
  ```
  ```MATLAB
  A = ...
  B = ...
  [M, N] = size(A);
  [~, L] = size(B);
  C = zeros(M, L);
  for m = 1:M
      for l = 1:L:
         for n = 1:N:
             C(m, l) += A(m, n) * B(n, l);
         end
      end
  end
  ```
  ```C++
  #include <iostream>
  #include <vector>

  using Matrix = std::vector<std::vector<double>>;

  Matrix A...;
  Matrix B...;
  
  size_t M = A.size();
  size_t N = A[0].size();
  size_t L = B[0].size();

  Matrix C(M, std::vector<double>(L, 0));

  for (size_t m = 0; m < M; ++n) {
    for (size_t l = 0; l < L; ++l) {
      for (size_t n = 0; n < N; ++n) {
        C[m][l] += A[m][n] * B[n][l];
      }
    }
  }
  ```
  ````
  :::::

  :::::{tab-item} Index
  \begin{gather*}
    C^{m}{}_{l} = A^{m}{}_{n}B^{n}{}_{l} 
  \end{gather*}
  Here we simplify the notation using the [Einstein summation convention][Einstein
  notation] where repeated indices are implicitly summed over (called **contraction**).
  *(In more complicated geometries (i.e. curved space-time), the vertical placement of
  indices is important: contractions are only valid between one upper and one lower index.)*

  ````{tab-set-code}
  ```python
  import numpy as np
  
  C = np.einsum('mn,nl->ml', A, B)
  ```
  ````
  :::::

  :::::{tab-item} Index-free: 
  
  \begin{gather*}
    \mat{C} = \mat{A}\mat{B}
  \end{gather*}

  ````{tab-set-code}
  ```python
  C = A @ B
  ```
  ```MATLAB
  C = A * B
  ```
  ```C++
  // Using the Armadillo library:
  // see https://en.wikipedia.org/wiki/Armadillo_(C++_library)
  #include <armadillo>
  
  arma::mat A = ...;
  arma::mat B = ...;
  arma::mat C = A*B;
  ```
  ````
  [^Sapir–Whorf]: The idea that language fundamentally influences how with think is
  known as the [Sapir–Whorf hypothesis][Linquistic relativity], or linguistic
  relativity.
  ::::::
  
  Which do you like better?  Index-free notations are clearly best for matrix
  multiplication, but with higher-dimensional tensors (more than 2 indices), the index
  notation and {func}`numpy.einsum` can be very helpful.  Likewise, if you need to do
  tricks like skipping through non-contiguous indices, or working with heterogeneous
  structures like trees and graphs, then explicit loops might be the only feasible
  option (but see [Awkward Array][]).
  ::::::::::
:::{margin}
I.e., thoughtful [yak shaving][] to improve long-term efficiency and quality without
[bikeshedding][].
:::
* **Development Process and Optimization:** Computers tend to be unforgiving of
  mistakes.  The software engineering community has developed **processes** to reduce
  these mistakes, and the same ideas can be used to **optimize** your research. For example,
  agile development, documentation, testing, and debugging all have analogous roles in a
  vibrant research program.  These ideas, espoused in [The Pragmatic Programmer][], will be
  emphasized in the course.  Students will be taught to optimize both their code, and their
  development practices, to minimize the time to solution while ensuring correctness.
  :::{margin}
  <iframe width="100%"
  src="https://www.youtube-nocookie.com/embed/PK_yguLapgA?playlist=PK_yguLapgA&loop=1&start=91&end=112&autoplay=1&rel=0&mute=1"
  title="Ariane 5 explosion" 
  frameborder="0" 
  allow="accelerometer;
         autoplay;
         clipboard-write; 
         encrypted-media; 
         gyroscope; 
         picture-in-picture; 
         web-share"
  referrerpolicy="strict-origin-when-cross-origin" 
  allowfullscreen></iframe>
  
  On 4 June 1996, [Ariane flight V88](https://en.wikipedia.org/wiki/Ariane_flight_V88)
  exploded due to an unhandled exception -- a $370 million dollar software error in part
  caused by copying code.  How can you be sure your code is going to work?  This class
  will show you techniques for validation include back-of-the envelope estimation,
  convergence testing, uncertainty quantification, and using physical analogies to
  intuitively understand the behaviour of your code.
  :::
* **Correctness:** Quickly getting results is good, but ensuring that those results are
  correct is even more important.  We will focus on verifying results with comprehensive
  testing and continuous integration (CI), enabling you to confidently use library codes
  and tools like LLMs (ChatGPT, CoPilot, etc.) to quickly get work done **correctly**.
* **Reproducible Research:** Software development practices like version control with
  [git][], [mercurial][], or [jujutsu][], can be repurposed to keep track of your papers
  and your research work (and to back them up in the cloud).  Coupled with good
  documentation, these enable **reproducible science**, both by others, and by your
  future self.
* **Thinking like a programmer** can help you approach problems differently and better
  use your tools.  Many repetitive or complex tasks can be solved by simple programming
  -- E.g., using [regular expressions][] in your editor to do a complicated search and
  replace, using [Makefiles][] to programmatically download and install software you
  need, using [LaTeX][] to write your thesis, having it automatically collect, organize,
  and format your references.


[yak shaving]: <https://en.wiktionary.org/wiki/yak_shaving>
[bikeshedding]: <https://en.wiktionary.org/wiki/bikeshedding>
[array programming]: <https://en.wikipedia.org/wiki/Array_programming>
[programming paradigms]: <https://en.wikipedia.org/wiki/Programming_paradigm>
[Linquistic relativity]: <https://en.wikipedia.org/wiki/Linguistic_relativity>
[MATLAB]: <https://en.wikipedia.org/wiki/MATLAB>
[NumPy]: <https://numpy.org>
[SciPy]: <https://scipy.org>
[Matplotlib]: <https://matplotlib.org/>
[python]: <https://www.python.org/>
[Einstein notation]: <https://en.wikipedia.org/wiki/Einstein_notation>
[Awkward Array]: <https://awkward-array.org>
[The Pragmatic Programmer]: <https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/>
[regular expressions]: <https://en.wikipedia.org/wiki/Regular_expression>
[Makefiles]: <https://makefiletutorial.com/>
[LaTeX]: <https://www.latex-project.org/>
[git]: <https://git-scm.com/>
[mercurial]: <https://www.mercurial-scm.org/>
[jujutsu]: <https://jj-vcs.github.io/jj/latest/>
[GitHub]: <https://github.com/>
[GitLab]: <https://gitlab.com/>

(sec:ProgrammingExamples)=
## Programming Examples

:::{margin}
Understanding the science behind these problems will take much more than hours, but the
goal of this course is to lower the computing barrier so that you can quickly explore
the science.
:::
Here are a few examples of the types of problems we will solve in this course.  Each of
these examples is code-complete, using only [NumPy][] and [SciPy][] to compute the
solution, and [Matplotlib][] to plot (with a custom function
{func}`~phys_581.contexts.FPS` to help make movies).  By the end of the course, you
should be able to write similar code within a matter of hours to study problems relevant
to your research.

:::{margin}
Finite element (FEM) techniques are the industry standard for modeling materials with
complicated (and expensive) packages like [COMSOL](https://www.comsol.com/).  Here you
will learn to directly model parts of these systems in ways that you fully understand
so you can validate and trust the results you get from black-box tools.
:::
### Molecular Dynamics

Complex material properties can be simply modeled with molecular dynamics simulations
that simply apply Newton's laws to a collection of particles.  Here we show a simple
example of a projectile impacting a slab.  See {ref}`sec:MolecularDynamics` for more
details.

This idea can be easily generalized for your research: e.g. orbiting planets, galaxy
formation, traffic flow, protein folding, and biomechanics.

:::{margin}
Dynamics of a small projectile hitting a beam.  Colours indicate the kinetic energy,
with yellow moving fastest.  Notice the shockaves being transmitted through the beam,
and an effective [Newton's cradle][] ejecting the particle at the top-right of the beam.

*This is a rather small example because the code (from scratch) is simple and
un-optimized, but must be run on the documentation server. In the course we will discuss
techniques for optimization so that much larger simulations can be studied.*

[Newton's cradle]: <https://en.wikipedia.org/wiki/Newton's_cradle>

:::
```{code-cell}
:tags: [hide-input]
#%%time
import numpy as np
_EPS = np.finfo(float).eps

class MolecularDynamics:
    L1 = 250
    L2 = 40
    epsilon = 5
    sigma = 1
    m = 1
    N1xy = (40, 40)
    N2xy = (160, 40)
    v1 = 0 - 10j
    v2 = 0 + 0j
    r_cut_sigma = 2.5
    dt = 0.00005
    
    sep = 0+5j  # Initial separation (units of dx)

    # For colouring
    kinetic_energy_max = 5
    cmap = plt.colormaps['viridis']

    def __init__(self, **kw):
        for key in kw:
            if not hasattr(self, key):
                raise ValueError(f"Unknown {key=}")
            setattr(self, key, kw[key])
        self.init()
    
    def init(self):
        self.dx = 2**(1/6) * self.sigma   # Equilibrium spacing
        self.z0_1 = (-self.N1xy[0]/2 + (self.N1xy[1]-1)*1j +  self.sep) * self.dx
        self.z0_2 = (-self.N2xy[0]/2 + 0j) * self.dx
        
    def F_r(self, r2):
        """Return F/r where F is the Lennard-Jones force for separation r.
        
        I.e., F_ij = (F/r)*r
        
        where r = r_j - r_i.
        
        Arguments
        =========
        r2 : array-like
            Square of the separation distance.
        """
        # Prevent divide-by-zero errors
        r2_ = r2 + _EPS
        # s = (sigma/r)**6
        s = np.where(r2 > 0, (self.sigma**2 / r2_)**2, 0)
        s *= s**2
        f = 24 * self.epsilon * s / r2_ * (1 - 2*s)
        return f
        
    def get_initial_state(self):
        (X1, Y1), (X2, Y2) = [
            np.meshgrid(*[np.arange(N)* self.dx + x0
                          for (N, x0) in zip(Nxy, (z0.real, z0.imag))],
                        indexing='ij')
            for Nxy, z0 in [(self.N1xy, self.z0_1), 
                            (self.N2xy, self.z0_2)]
        ]
        Z1 = X1 + 1j*Y1
        Z2 = X2 + 1j*Y2
        V1 = 0*Z1 + self.v1
        V2 = 0*Z2 + self.v2
        Z, V = [np.concatenate(list(map(np.ravel, A)))
                for A in ((Z1, Z2), (V1, V2))]
        return (0, Z, V, 0*Z)
    
    def plot(self, state, ax):
        t, z, v, a = state
        kinetic_energy = self.m * (v.conj()*v).real / 2
        ax.scatter(z.real, z.imag, c=self.cmap(kinetic_energy/self.kinetic_energy_max))
        ax.set(title=f"{t=:.4f}", aspect=1)
        return ax
    
    def get_F(self, z, v):
        """Return the total force on particles at z."""
        r_ = z[None, :] - z[:, None]
        return (self.F_r(r2=abs(r_)**2) * r_).sum(axis=1)
        
    def step(self, state):
        """Return the acceleration on each particle.
        
        This uses the Velocity-Størmer-Verlet method.
        """
        dt = self.dt
        t0, z0, v0, a0 = state
        z1 = z0 + dt * v0 + dt**2/2 * a0
        a1 = self.get_F(z1, v0) / self.m
        v1 = v0 + dt / 2 * (a0 + a1)
        return (t0 + dt, z1, v1, a1)


md = MolecularDynamics(
    v1=-5j, N1xy=(4*2, 4*2), N2xy=(16*2, 4*2), sep=-10.3+2j,
    kinetic_energy_max=10, dt=0.00005*40)
s = md.get_initial_state()

fig, ax = plt.subplots(layout="constrained")
for frame in FPS(100, fig=fig, embed=True, filename="_static/MD.mp4"):
    for n in range(20):
        s = md.step(s)
    ax.cla()
    ax = md.plot(s, ax=ax)
    ax.set(xlim=(-12*2, 12*2), ylim=(-5, 10*2))
    ax.set_axis_off()
    fig.colorbar
```

### Shallow-Water Equations
Here is another complete example of one of the core applications that will be taught in the
course: solving the Navier-Stokes equations in 1D for incompressible fluid flowing over
a rough surface.  In this example, we excite a fluid in a harmonic trap with a bump.  As
the fluid flows back and forth over the bump, viscosity dissipates energy and the
sloshing slows down.

This idea can be easily generalized for your research: e.g. economic models, quantum
mechanics, and biological response.

```{code-cell}
:tags: [hide-input]
#%%time
# Example using a pseudo-spectral Chebyshev basis to solve the shallow-water
# equations for an oscillating fluid.  This code is complete, using only
# NumPy and SciPy except for plotting, which uses Matplotlib and a custom
# function FPS to facilitate making movies.
#
# Not included, but part of the class code, will be supporting code for
# testing the results, and better organization to facilitate reuse.

import numpy as np, matplotlib.pyplot as plt
import scipy.fft, scipy.integrate
sp = scipy

Nx = 128
th0 = (2*np.arange(Nx) + 1) * np.pi / 2 / Nx
k = np.arange(Nx)
x0 = np.cos(th0)

g_m = 1.0  # Coupling constant g/m
nu = 0.2  # Viscosity coefficient

w = np.sqrt(2)  # Trap frequency
V0_m = 0.5  # Bump height
sigma = 0.1  # Bump width

# Initial density provile 
X0_shift = 0.5  # Initial shift of the cloud
X0 = x0 + X0_shift

# Initial density provile 
n0 = w**2/2/g_m * (1 - x0**2)
n0_x0 = -w**2 / g_m * x0

def diff(f, d=1, k=k, th=th0):
    """Return the dth derivative of d.
    
    The derivative here is computed using the discrete cosine and
    sine transforms associated with the Chebyshev peudospectral basis.
    """
    ft = sp.fft.dct(f, type=2, norm='forward')
    s = np.sin(th)

    # Shift the coefficients and pad with zero.
    df_dth = sp.fft.dst(np.concatenate([-(ft*k)[1:], [0]]), type=3)
    if d == 1:
        df_dx = -df_dth / s
        return df_dx 
    elif d == 2:
        d2f_dth2 = sp.fft.idct(-ft*k**2, type=2, norm='forward')
        d2f_dx2 = (d2f_dth2 - df_dth / np.tan(th)) / s**2
        return d2f_dx2
    return diff(df_dx, d=d-1)
    
def pack(X, X_t):
    """Return a single 1D array with X and X_t."""
    return np.ravel([X, X_t])

def unpack(y):
    """Return (X, X_t) from y."""
    return np.reshape(y, (2, Nx))

def unpack_xnu(y):
    """Return (x, n, u) from y."""
    X, X_t = unpack(y)
    X_x0 = np.array([diff(x) for x in X])
    n = n0 / X_x0
    u = X_t
    x = X
    return x, n, u

def compute_dy_dt(t, y):
    """Return pack(X_t, X_tt), the rhs of the ODE."""
    X, X_t = unpack(y)
    X_x0 = diff(X)
    X_x0x0 = diff(X, d=2)
    n_x = n0_x0 / X_x0**2 - n0 * X_x0x0 / X_x0**3
    
    u_xx = (diff(X_t, d=2)) / X_x0**2 - diff(X_t) * X_x0x0 / X_x0**3
    u_xx[0] = u_xx[-1] = 0  # Zero-laplacian boundary conditions
    
    a_ext = - w**2 * X
    a_ext += V0_m * np.exp(-X**2/2/sigma**2) * X/sigma**2
    X_tt = a_ext - g_m * n_x + nu * u_xx
    return pack(X_t, X_tt)


Tw = 2*np.pi / w  # Trapping period
T = 20*Tw
Nt = 200  # Number of evaluation times
t_eval = np.linspace(0, T, Nt)

y0 = pack(X0, 0*X0)  # Packed initial state
res = sp.integrate.solve_ivp(
    compute_dy_dt, y0=y0, t_span=(0, T), t_eval=t_eval, method="BDF")

if not res.success:
    print(res.message)

# Plot the results and make a movie.
import matplotlib.pyplot as plt

from phys_581.contexts import FPS

ts = res.t

# Get the density from the solution
X, X_t = res.y.reshape((2, Nx, Nt))
X_x0 = np.transpose([diff(x) for x in X.T])
n = n0[:, None] / X_x0
    
# The following is a little complicated - it translates from the hydrodynamic
# picture with density n into the shallow-water picture with a fluid of height
# h above the stream bed of hight b.  The latter is more familiar (water in a glass)
# than the compressible-gas fluid dynamics, allowing us to better assess
# the qualitative nature of the solution

x_ = np.linspace(X.min(), X.max(), 200)
V_ext = w**2 * X**2 / 2
V_ext += V0_m * np.exp(-X**2/2/sigma**2)
b = V_ext / g_m

b_ = w**2 * x_**2 / 2
b_ += V0_m * np.exp(-x_**2/2/sigma**2)
b_ /= g_m 

h = b + n

fig, ax = plt.subplots(figsize=(6, 3), layout="constrained")
for i in FPS(Nt, fig=fig, embed=True, fps=10, filename="_static/SW.mp4"):
    t = ts[i]
    ax.cla()
    ax.plot(x_, b_, 'k-')
    ax.fill_between(X[:, i], b[:, i], h[:, i], color='cyan')
    ax.set(title=rf"$t={t/Tw:.2f}T_{{\omega}}$",
           ylabel="Height", xlabel="$x$",
           xlim=(X.min(), X.max()), ylim=(0, h.max()));
```

:::{admonition} Details for those interested: The Shallow-Water Equations 
:class: dropdown

The relevant equations follow from Newton's law $F=ma$, where the acceleration $a =
\ddot{X}$ of a particle at position $x$ and time $t$ is due to the external potential
$V(x)$, an internal energy $gn(x,t)$ related to the hydrostatic pressure, and a viscous
damping term with coefficient $\nu$:
\begin{gather*}
  \ddot{X}(t) = \frac{F}{m} = -\frac{\nabla \bigl(V(x) + gn(x,t)\bigr)}{m} + \nu
  \nabla^2 \dot{X}
\end{gather*}
This corresponds to an Eulerian hydrodynamic description of a compressible fluid with
particle-density $n(x, t)$ and velocity $u(x, t)$ through the following
partial-differential equations ([PDE][]s)
\begin{align*}
  \pdiff{n(x,t)}{t} + \pdiff{n(x,t) u(x,t)}{x} &= 0\\
  \pdiff{u(x,t)}{t} + u(x,t)\pdiff{u(x,t)}{x} 
  &= -\pdiff{}{x}\frac{V(x) + g n(x,t)}{m}
  + \nu\pdiff[2]{u(x, t)}{x}
\end{align*}
that can be solved by the [method of lines][] as a system of [ODE][]s integrating forward in time.

We will start with a simple implementation using finite-differences and
Euler's method to get an intuition for what is happening and to check our results:
\begin{gather*}
  \pdiff{f}{x} \approx \frac{f(x+\delta x) - f(x-\delta x)}{2\delta xh}, \qquad
  f(t+\delta t) \approx f(t) + \delta t \dot{f},
\end{gather*}
but then refine our solution using pseudo-spectral methods for derivatives and adaptive
integration to get high-precision solutions.  This latter solution is completed coded
below to give you a flavour of the type of code that will be developed in the course.

*There is some physics needed to describe details about this problem, like how the
density $n(x, t)$ relates to the positions of the particles $X(t)$ in the first
description, and how the internal energy $gn(x, t)$ is related to the hydrostatic
pressure.  For details, see my
[Viscosity Notes 3](https://gpe.readthedocs.io/en/latest/Notes/Viscosity_3.html) and
[Viscosity Notes 4](https://gpe.readthedocs.io/en/latest/Notes/Viscosity_4.html).*

The code here demonstrates the use of a pseudo-spectral Chebyshev basis to solve
the shallow-water equations for an oscillating fluid.  This code is complete, using only
[NumPy][] and [SciPy][] libraries, except for plotting, which uses [Matplotlib][] and a
custom function FPS to facilitate making movies. 

Not included here, but part of the class, will be supporting code for testing,
additional documentation, and better organization to facilitate reuse.
:::

For another example, see {ref}`sec:ModelFitting`.


:::{margin}
<iframe width="100%"
        src="https://www.youtube-nocookie.com/embed/ry55--J4_VQ" 
        title="Apollo 13: Square peg in a round hole" 
        frameborder="0" 
        allow="accelerometer; 
               autoplay; 
               clipboard-write; 
               encrypted-media; 
               gyroscope; 
               picture-in-picture; 
               web-share" 
        referrerpolicy="strict-origin-when-cross-origin"
        allowfullscreen></iframe>

*Scene from Apollo 13 where a team of NASA engineers scramble to solve a life-or-death
problem, saving the lives of the astronauts.  This course will explore agile development
techniques inspired by problems like this, where rapid exploration is needed to solve a
real-world problem in a highly interactive and collaborative environment.*
:::

(sec:LearningOutcomes)=
# Learning Outcomes

In addition to the {ref}`sec:SpecificSkills` listed in the {ref}`sec:Syllabus`, by the end
of the course, students should be able to:

* Produce publication quality figures and animations (e.g., 2D and 3D plots, animations,
  tables, etc.) following best practices of visualization of quantitative data (i.e., as
  espoused by E. Tufte.)
* Produce a manuscript meeting all formatting requirements for a paper in their field of
  focus, or for a thesis.  (E.g., using a suitable LaTeX template and conforming to the
  submission requirements for mathematics, figures, citations, typesetting, etc.)
* Work with data in a variety of formats for analysis, sharing, and reproducible
  science.
* Use version control system to managing code or documents, and establish a portfolio of
  code on sites like [GitLab][] or [GitHub][] with testing via continuous integration
  (CI) and documentation.
* Profile and optimize their code.
* Use a code editor that supports regexp search-and-replace, linting, checking,
  etc. (E.g., Emacs, Vi, VS Code).

* Numerically compute integrals and derivatives with quantified error estimates.
* Use numerical linear algebra libraries to diagonalize and factorize matrices: QR, SVD,
  Cholesky.
* Generate pseudo-random numbers with any desired distribution.
* Use MCMC techniques to characterize and quantify Bayesian posteriors for fitting and
  model selection.
* Use local optimization techniques (gradient descent, Nelder-Meade, BFGS,
  Broyden) with and without gradients.
* Use techniques like Runge-Kutta to solve initial value problems.
* Derive scaling results to validate these techniques.
  
[$k$-d trees]: <https://en.wikipedia.org/wiki/K-d_tree>
[Courant-Freidrichs-Lewy condition]: <https://en.wikipedia.org/wiki/Courant%E2%80%93Friedrichs%E2%80%93Lewy_condition>

## Notes
This is a live working document hosted on [Read The
Docs](https://wsu-phys-581-computation.readthedocs.io/en/latest) that will be used to
collect and display additional information about the course, including:
* {ref}`sec:Syllabus`
* {ref}`sec:assignments`
* {ref}`sec:readings`
and various class notes.  These should also be available through the navigation menu on
the left (which might hidden if your display is not sufficiently wide).

These documents are built using [JupyterBook][] (see {ref}`sec:demonstration`) and
include all of the source code needed to generate the figure, plots etc.  For example,
to see how a figure was made, look in the preceding code cell.
The complete source code for this documentation is available at
<https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation>. 

## Funding Statement
<a href="https://www.nsf.gov"><img width="10%"
src="https://nsf.widen.net/content/txvhzmsofh/png/" />
</a>
<br>

Some of the material presented here is based upon work supported by the National Science
Foundation under [Grant Number 2309322](https://www.nsf.gov/awardsearch/showAward?AWD_ID=2309322). Any opinions,
findings, and conclusions or recommendations expressed in this material are those of the
author(s) and do not necessarily reflect the views of the National Science Foundation.
 
<!-- We use a hack here to get a relative link in the TOC.  
     This will fail with LaTeX etc.
     https://stackoverflow.com/a/31820846/1088938 
     https://github.com/sphinx-doc/sphinx/issues/701
     
```{toctree}
---
maxdepth: 2
caption: "Contents:"
titlesonly:
hidden:
---
GettingStarted
Syllabus
Syllabus_Prerequisites
References
Assignments
api/index
```
-->
```{toctree}
---
maxdepth: 2
caption: "Contents:"
titlesonly:
hidden:
---
Syllabus
References
ClassLog
Assignments/Assignment-0
Assignments/Assignment-1
Assignments/Assignment-3
Assignments/Assignment-4
Assignments/Assignment-5
```

```{toctree}
---
maxdepth: 2
caption: "Readings and Notes:"
titlesonly:
hidden:
---
Notes/RecursionAndInvariants
Notes/ExecutionTime
Notes/GloballyConvergentNewton
Notes/Derivatives
Notes/ODEs
Notes/RungeKutta
Notes/FourierTechniques
Notes/SEQ
Notes/SEQNotes
Notes/TypesAndClasses
Notes/Documentation
Notes/Graphs
```

```{toctree}
---
maxdepth: 2
caption: "Prerequisites:"
titlesonly:
hidden:
glob:
---
Prerequisites/*
```

```{toctree}
---
maxdepth: 2
caption: "Programming:"
titlesonly:
hidden:
glob:
---
CodingStyle
```

```{toctree}
---
maxdepth: 2
caption: "Tools, Languages, and Compilers:"
titlesonly:
hidden:
glob:
---
Tools/*
Performance/*
Assignments/*
```


```{toctree}
---
maxdepth: 2
caption: "Other Content:"
hidden:
---
Notes
InstructorNotes
Demonstration
CoCalc
PythonPackaging
project_seq/Notes
```

<!-- This includes another README.md rendering that will get updated if the file is
     changed so that we can see updates when using make doc-sever.  The reason is that,
     because we generate the main page using a literal include ({include} README.md...),
     the main page will only get updated if we change this index.md file.
     
     We do not include this extra link when we build on RTD.  We do this using the
     sphinx.ext.ifconfig extension:
     
     https://www.sphinx-doc.org/en/master/usage/extensions/ifconfig.html

```{eval-rst}
.. ifconfig:: not on_rtd

   .. toctree::
      :maxdepth: 0
      :caption: "Includes (for autobuild):"
      :titlesonly:
      :hidden:

      README
   
```
-->

[JupyterBook]: <https://jupyterbook.org>
[ODE]: <https://en.wikipedia.org/wiki/Ordinary_differential_equation>
[PDE]: <https://en.wikipedia.org/wiki/Partial_differential_equation>
[method of lines]: <https://en.wikipedia.org/wiki/Method_of_lines>

