```python
%pylab inline
```

    Populating the interactive namespace from numpy and matplotlib

```python
from sympy.parsing.sympy_parser import parse_expr
from sympy import Matrix, symbols, expand
```

```python
xs = np.linspace(-1,1,32)
ys = np.linspace(-1,1,32)
X, Y = np.meshgrid(xs, ys)
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111)
ax.set_aspect(1)
mat = np.array([[.87,-.2],[.20,2]])
X, Y = zip(*[np.matmul(mat, np.array([x, y])) for x, y in zip(X.ravel(), Y.ravel())])
plot(X,Y,'o',c='purple',markersize=4);
axhline(0, lw=1, c='black')
axvline(0, lw=1, c='black')
xlim(-2,2)
ylim(-2,2)
plt.axis("on")
#xlim(-2,2)
#ylim(-2,2)
```

```python
mat = np.array([[.5,0],[0,2]])
```

```python
xs, ys = zip(*[np.matmul(mat, np.array([x, y])) for x, y in zip(X.ravel(), Y.ravel())])
plot(xs,ys,'o',c='purple',markersize=4);
```

```python
np.matmul(mat, np.array([1,0]))
```

    array([2, 2])

```python
np.dot(mat, np.array([1,1]))
```

    array([4, 4])

```python
def interpolate(M,N,alpha): return (1-alpha) * M + alpha * N
```

```python
interpolate(mat, np.eye(2), 0.5)
```

    array([[0.75, 0.  ],
           [0.  , 1.5 ]])

```python
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(16,10))

angle = 1/2*np.pi
mat = np.array([[np.cos(angle), np.sin(angle)],[ -np.sin(angle), np.cos(angle)]])
xs = np.linspace(-1,1,32) #np.array([1,-1])
ys = np.linspace(-1,1,32) #np.array([1,-1])
X, Y = np.meshgrid(xs, ys)
N = 6
for n in range(0, N+1):
    ax = fig.add_subplot(N+1, 1, n+1)
    ax.set_aspect(1)
    #plt.axis([0, 4, 0, 4])

    xs, ys = zip(*[np.matmul( interpolate(np.eye(2), mat, n/N), np.array([x, y])) for x, y in zip(X.ravel(), Y.ravel())])

    plot(xs,ys,'o',c='purple',markersize=4);
    axhline(0, lw=1, c='black')
    axvline(0, lw=1, c='black')
    plt.axis("off")

plt.tight_layout(pad=0)

plt.show()
```

```python
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
fig.set_tight_layout(True)

# Query the figure's on-screen size and DPI. Note that when saving the figure to
# a file, we need to provide a DPI for that separately.
print('fig size: {0} DPI, size in inches {1}'.format(
    fig.get_dpi(), fig.get_size_inches()))

# Plot a scatter that persists (isn't redrawn) and the initial line.
x = np.arange(0, 20, 0.1)
ax.scatter(x, x + np.random.normal(0, 3.0, len(x)))
line, = ax.plot(x, x - 5, 'r-', linewidth=2)

def update(i):
    label = 'timestep {0}'.format(i)
    print(label)
    # Update the line and the axes (with a new xlabel). Return a tuple of
    # "artists" that have to be redrawn for this frame.
    line.set_ydata(x - 5 + i)
    ax.set_xlabel(label)
    return line, ax

if __name__ == '__main__':
    # FuncAnimation will call the 'update' function for each frame; here
    # animating over 10 frames, with an interval of 200ms between frames.
    anim = FuncAnimation(fig, update, frames=np.arange(0, 10), interval=200)
    anim.save('line.gif', dpi=80, writer='imagemagick')
```

```python

```

Applied Math/Y2S2/.ipynb_checkpoints/transformation plot 2d-checkpoint.ipynb


### Importing packages

#### Others

```python
# Used to read the data file
import pandas as pd

# Used in the definition of the model and its initial condition
import numpy as np

# Used for plots other than those generated by the parameter estimation module
import matplotlib.pyplot as plt
%matplotlib inline
```

#### The module itself

```python
import PDEparams as pde
```

### Defining the model

We use the two-dimensional model of tumour growth from Eq. (5) of [Anderson *et al.* (2000)](http://dx.doi.org/10.1080/10273660008833042):

$$\begin{align}
\frac{\partial n}{\partial t} &= d_n \nabla^2 n - \gamma \nabla \cdot (n \nabla f)\\
\frac{\partial f}{\partial t} &= - \eta m f\\
\frac{\partial m}{\partial t} &= d_m \nabla^2 m + \alpha n - \beta m
\end{align}$$

This model describes the invasion and degradation of surrounding host tissue by tumour cells. The latter are represented in the model by $n$, while $f$ is the surrounding tissue or *extracellular matrix* (ECM); $m$, in turn, corresponds to *matrix degradative enzymes* (MDEs), which are activated by the tumour cells and degrade the ECM, as their name implies.

**Note:** in the original simulations, the parameter $\beta$ is set to zero. We do the same here.

#### Boundary conditions

We use no-flux boundary conditions in the 2D domain defined by the data. Eqs. (6)-(7) of Anderson *et al.* (2000) translate to

$$\frac{\partial n}{\partial x} = \frac{\gamma n}{d_n}\frac{\partial f}{\partial x}$$

at the $x$ boundaries; and equivalently for $y$.

Note that the equation for $f$ does not have spatial derivatives, and therefore no boundary conditions. In the code below, the derivatives of $f$ entering the equation for $n$ are computed directly, and then plugged into the condition for the first-order derivative of $n$.

#### Second-order derivatives of $n$ and $m$

In practice, if we consider the variable $u$ ($n$ or $m$) as a discrete 1D vector $(u_0, u_1, ...)$, then we can obtain the central differences

$$\begin{align}
h\, u'_i &= u_{i+1}-u_{i-1}\\
h^2\, u''_i &= u_{i+1}+u_{i-1}-2u_i
\end{align}$$

where $h=dx$, $u' = \partial u/\partial x$, and $u'' = \partial^2 u/\partial x^2$. If we have a boundary condition $u'_0 = \sigma$, and we take an imaginary point $u_{-1}$ to the left of our domain so that

$$h^2\, u''_0 = u_{1}+u_{-1}-2u_0$$

at the boundary, we obtain from the first-order difference $u_{-1} = u_1 - h \sigma$. Therefore, in our case

$$\begin{align}h^2n''_0 &= 2n_1 - h \gamma n_0 f'_0/d_n - 2n_0\\
h^2m''_0 &= 2m_1 - 2m_0
\end{align}$$

The same idea applies in the 2D case to the four boundaries.

```python
def TumourGrowth(z, t, grid, alpha, gamma):

    '''The input z corresponds to the current state of the system, and it's a flattened vector in both the number
    of outputs and the spatial dimensions.

    t is the current time.

    grid is the spatial grid of the model.

    alpha and gamma correspond to the unknown parameters.
    '''

    # Known parameter values (same as in the paper)

    dN = 1e-3
    dM = 1e-3
    eta = 10.

    '''Here we obtain both functions by reshaping the input: we divide it into 3 portions (number of outputs)
    of whatever shape the spatial grid has (this is what the -1 stands for)'''

    N, F, M = z.reshape(3,-1)

    '''Now we reshape both functions using the actual dimensions of the spatial grid.

    The shape of the spatial grid array is given by:

                        (number of points in dim 1, number of points in dim 2, ..., ndims)

    with ndims the number of spatial dimensions. This is due to the fact that we have a grid of

                        (number of points in dim 1)x(number of points in dim 2)

    elements, but each element is ndims-dimensional. Therefore, we must take the first "ndims" elements of the
    shape of the grid, ignoring the last one, to reconstruct the shape of each function. The slice with [:-1]
    stands for "all elements up to and excluding the last one".
    '''

    N = N.reshape(grid.shape[:-1])

    F = F.reshape(grid.shape[:-1])

    M = M.reshape(grid.shape[:-1])

    '''The grid has the form

                (x0,y0), (x0,y1), ...
                (x1,y0), (x1,y1), ...

    So that:
        x0 = first element of the first element of the first row of the grid: grid[0,0,0]
        x1 = first element of the first element of the second row of the grid: grid[1,0,0]
        y0 = second element of the first element of the first row of the grid: grid[0,0,1]
        y1 = second element of the second element of the first row of the grid: grid[0,1,1]
    '''

    dx = grid[1,0,0]-grid[0,0,0]
    dy = grid[0,1,1]-grid[0,0,1]

    # We initialise the spatial derivatives we need as empty arrays of the same shape of our N and L functions

    dNdx = np.empty_like(N)
    dNdy = np.empty_like(N)
    dNdxx = np.empty_like(N)
    dNdyy = np.empty_like(N)

    dMdxx = np.empty_like(M)
    dMdyy = np.empty_like(M)

    #First-order derivatives

    # np.gradient(array, axis) returns centred 1st-order differences between values along x (rows, axis=0)
    # or y (columns, axis=1), plus forward/backward differences for the left/right boundaries
    dFdx = np.gradient(F, axis=0)/dx
    dFdy = np.gradient(F, axis=1)/dy

    # Left boundary. In the grid array, first row, all columns: [0,:]
    dNdx[0,:] = gamma*N[0,:]*dFdx[0,:]/dN

    # Away from the x-boundary. In the grid array, second to penultimate row, all columns: [1:-1,:]
    dNdx[1:-1,:] = np.gradient(N, axis=0)[1:-1,:]/dx

    # Right boundary. In the grid array, last row, all columns: [-1,:]
    dNdx[-1,:] = gamma*N[-1,:]*dFdx[-1,:]/dN

    # Bottom boundary. In the grid array, all rows, first column: [:,0]
    dNdy[:,0] = gamma*N[:,0]*dFdy[:,0]/dN

    # Away from the y-boundary. In the grid array, all rows, second to penultimate columns: [:,1:-1]
    dNdy[:,1:-1] = np.gradient(N, axis=1)[:,1:-1]/dy

    # Right boundary. In the grid array, all rows, last column: [:,-1]
    dNdy[:,-1] = gamma*N[:,-1]*dFdy[:,-1]/dN

    # Second-order derivatives

    dFdxx = np.gradient(dFdx, axis=0)/dx
    dFdyy = np.gradient(dFdy, axis=1)/dy

    # Left boundary. In the grid array, first row, all columns: [0,:]
    dNdxx[0,:] = (2.0*N[1,:] - dx*gamma*N[0,:]*dFdx[0,:]/dN - 2.0*N[0,:])/dx**2

    # Away from the x-boundary. In the grid array, second to penultimate row, all columns: [1:-1,:]
    # np.diff(array, order, axis) is used to compute 2nd-order differences between values along x, or rows (axis=0)
    dNdxx[1:-1,:] = np.diff(N,2,axis=0)/dx**2

    # Right boundary. In the grid array, last row, all columns: [-1,:]
    dNdxx[-1,:] = (2.0*N[-2,:] - dx*gamma*N[-1,:]*dFdx[-1,:]/dN - 2.0*N[-1,:])/dx**2

    # Bottom boundary. In the grid array, all rows, first column: [:,0]
    dNdyy[:,0] = (2.0*N[:,1] - dy*gamma*N[:,0]*dFdy[:,0]/dN - 2.0*N[:,0])/dy**2

    # Away from the y-boundary. In the grid array, all rows, second to penultimate columns: [:,1:-1]
    # np.diff(array, order, axis) is used to compute 2nd-order differences between values along y, or columns (axis=1)
    dNdyy[:,1:-1] = np.diff(N,2,axis=1)/dy**2

    # Right boundary. In the grid array, all rows, last column: [:,-1]
    dNdyy[:,-1] = (2.0*N[:,-2] - dy*gamma*N[:,-1]*dFdy[:,-1]/dN - 2.0*N[:,-1])/dy**2

    # Now the same for M

    dMdxx[0,:] = (2.0*M[1,:] - 2.0*M[0,:])/dx**2
    dMdxx[1:-1,:] = np.diff(M,2,axis=0)/dx**2
    dMdxx[-1,:] = (2.0*M[-2,:] - 2.0*M[-1,:])/dx**2

    dMdyy[:,0] = (2.0*M[:,1] - 2.0*M[:,0])/dy**2
    dMdyy[:,1:-1] = np.diff(M,2,axis=1)/dy**2
    dMdyy[:,-1] = (2.0*M[:,-2] - 2.0*M[:,-1])/dy**2

    # Adding the rest of the terms to obtain the time derivatives

    dNdt = dN*(dNdxx + dNdyy) - gamma*(dNdx*dFdx + dNdy*dFdy + N*(dFdxx + dFdyy))
    dFdt = -eta*M*F
    dMdt = dM*(dMdxx + dMdyy) + alpha*N

    # We put the three time derivatives side by side

    dzdt = np.array([dNdt, dFdt, dMdt])

    return dzdt.reshape(-1) # We return a completely flattened version of the total time derivative
```

Here we specify the functions that define the initial condition for each variable—Eq. (9) of Anderson *et al.* (2000).

For this example, we used an arbitrarily chosen heterogeneous cellular matrix to generate the data, as in Fig. 8 of the paper.

```python
def initial_N(z, centre=[0.5,0.5], rad=0.1):
    epsilon = 2.5e-3
    x, y = z

    r = np.sqrt((x - centre[0])**2 + (y - centre[1])**2)

    if r <= rad:
        return np.exp(-r**2/epsilon)

    return 0.

# Homogeneous ECM (only for reference)
def initial_F(z):
    return 1. - 0.5*initial_N(z)

# Heterogeneous ECM
def initial_F2(z):
    return (1. - initial_N(z, rad=0.5) - 0.5*initial_N(z, centre=[0.8,0.8], rad=0.2)
            - 0.8*initial_N(z, centre=[0.2,0.2], rad=0.1) - 0.6*initial_N(z, centre=[0.05,0.6], rad=0.3)
            - 0.3*initial_N(z, centre=[0.6,0.1], rad=0.05) - initial_N(z, centre=[0.6,0.6], rad=0.5)
            - initial_N(z, centre=[0.8,0.4], rad=0.5) - initial_N(z, centre=[0.9,0.2], rad=0.5)
            - initial_N(z, centre=[0.3,0.8], rad=0.7))

def initial_M(z):
    return 0.5*initial_N(z)
```

### Using `PDEparams` to estimate parameters

First, we load the data from the `.csv` file.

The data consist of 3 replicates, and have been generated using parameter values $\alpha=0.1$, $\gamma=0.005$, as in the paper.

The columns are, in order: $t$, $x$, $y$, $n$, $f$, $m$.

```python
df = pd.read_csv('TumourGrowthData.csv')

df.head()
```

      0
      1
      2
      3
      4
      5

      0
      1.0
      0.0
      0.020408
      0.001154
      1.001187
      0.000670

      1
      1.0
      0.0
      0.020408
      0.000828
      0.998376
      0.000000

      2
      1.0
      0.0
      0.020408
      0.000000
      0.998738
      0.001314

      3
      1.0
      0.0
      0.061224
      0.001059
      1.000115
      0.000000

      4
      1.0
      0.0
      0.061224
      0.000886
      0.999843
      0.000075

#### Constructing the `PDEmodel` object.

The inputs are

**Required:**
1. The data table `df`.
2. The model `TumourGrowth`.
3. The list of initial condition functions.
4. The bounds for the parameter values.

**Optional:**
1. The parameter names.
2. The number of variables: 3. **(Default is 1, this needs to be provided in this case)**
3. The number of spatial dimensions: 2. **(Default is 1, this needs to be provided in this case)**
4. The number of replicates in the data: 3. **(Default is 1, this needs to be provided in this case)**
5. The indices of the measured variables. In this case, the default `None`, since we have data for all 3 variables.
6. The function to apply to the output. In this case, the default `None`, since our data is directly $n$, $f$ and $m$.

```python
my_model = pde.PDEmodel(df, TumourGrowth, [initial_N, initial_F2, initial_M],
                        bounds=[(0.05, 0.15), (0.002,0.01)], param_names=[r'$\alpha$', r'$\gamma$'],
                        nvars=3, ndims=2, nreplicates=3, obsidx=None, outfunc=None)
```

Let us plot the initial condition.

```python
X = my_model.space[:,:,0] # all x values
Y = my_model.space[:,:,1] # all y values

plt.contourf(X,Y,my_model.initial_condition[0], cmap='magma')
plt.colorbar()
plt.title(r'$n(x,y,0)$')
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
plt.show()

plt.contourf(X,Y,my_model.initial_condition[1], cmap='magma')
plt.colorbar()
plt.title(r'$f(x,y,0)$')
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
plt.show()

plt.contourf(X,Y,my_model.initial_condition[2], cmap='magma')
plt.colorbar()
plt.title(r'$m(x,y,0)$')
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
plt.show()
```

#### Finding the best fit parameters.

```python
%%time
my_model.fit()
```

       $\alpha$  $\gamma$
    0  0.103305  0.005293
    CPU times: user 1min 42s, sys: 1.98 s, total: 1min 44s
    Wall time: 1min 39s

```python
my_model.best_params
```

      $\alpha$
      $\gamma$

      0
      0.103305
      0.005293

```python
my_model.best_error
```

    1.3730150561063116e-05

#### Likelihood profiles

We use a grid of 25 points per parameter.

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model.likelihood_profiles(npoints=25)
```

    HBox(children=(IntProgress(value=0, description='parameters', max=2, style=ProgressStyle(description_width='in…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    CPU times: user 1h 21min 4s, sys: 8min 19s, total: 1h 29min 23s
    Wall time: 1h 2min 39s

The raw results

```python
my_model.result_profiles
```

      parameter
      value
      error

      0
      $\alpha$
      0.050000
      0.001016

      1
      $\alpha$
      0.054167
      0.000785

      2
      $\alpha$
      0.058333
      0.000602

      3
      $\alpha$
      0.062500
      0.000460

      4
      $\alpha$
      0.066667
      0.000345

      5
      $\alpha$
      0.070833
      0.000254

      6
      $\alpha$
      0.075000
      0.000182

      7
      $\alpha$
      0.079167
      0.000127

      8
      $\alpha$
      0.083333
      0.000086

      9
      $\alpha$
      0.087500
      0.000056

      10
      $\alpha$
      0.091667
      0.000035

      11
      $\alpha$
      0.095833
      0.000022

      12
      $\alpha$
      0.100000
      0.000015

      13
      $\alpha$
      0.104167
      0.000014

      14
      $\alpha$
      0.108333
      0.000017

      15
      $\alpha$
      0.112500
      0.000023

      16
      $\alpha$
      0.116667
      0.000033

      17
      $\alpha$
      0.120833
      0.000045

      18
      $\alpha$
      0.125000
      0.000059

      19
      $\alpha$
      0.129167
      0.000074

      20
      $\alpha$
      0.133333
      0.000092

      21
      $\alpha$
      0.137500
      0.000110

      22
      $\alpha$
      0.141667
      0.000130

      23
      $\alpha$
      0.145833
      0.000151

      24
      $\alpha$
      0.150000
      0.000172

      25
      $\gamma$
      0.002000
      0.000248

      26
      $\gamma$
      0.002333
      0.000197

      27
      $\gamma$
      0.002667
      0.000154

      28
      $\gamma$
      0.003000
      0.000118

      29
      $\gamma$
      0.003333
      0.000088

      30
      $\gamma$
      0.003667
      0.000064

      31
      $\gamma$
      0.004000
      0.000044

      32
      $\gamma$
      0.004333
      0.000030

      33
      $\gamma$
      0.004667
      0.000021

      34
      $\gamma$
      0.005000
      0.000015

      35
      $\gamma$
      0.005333
      0.000014

      36
      $\gamma$
      0.005667
      0.000016

      37
      $\gamma$
      0.006000
      0.000022

      38
      $\gamma$
      0.006333
      0.000031

      39
      $\gamma$
      0.006667
      0.000043

      40
      $\gamma$
      0.007000
      0.000057

      41
      $\gamma$
      0.007333
      0.000074

      42
      $\gamma$
      0.007667
      0.000093

      43
      $\gamma$
      0.008000
      0.000114

      44
      $\gamma$
      0.008333
      0.000138

      45
      $\gamma$
      0.008667
      0.000162

      46
      $\gamma$
      0.009000
      0.000188

      47
      $\gamma$
      0.009333
      0.000216

      48
      $\gamma$
      0.009667
      0.000245

      49
      $\gamma$
      0.010000
      0.000274

Visualisation

```python
my_model.plot_profiles()
```

#### Bootstrapping

We use 50 repetitions.

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model.bootstrap(nruns=50)
```

    HBox(children=(IntProgress(value=0, description='runs', max=50, style=ProgressStyle(description_width='initial…

            $\alpha$   $\gamma$
    count  50.000000  50.000000
    mean    0.103383   0.005287
    std     0.000092   0.000007
    min     0.103137   0.005275
    25%     0.103337   0.005283
    50%     0.103400   0.005286
    75%     0.103443   0.005291
    max     0.103549   0.005304
    CPU times: user 1h 13min 4s, sys: 2min 24s, total: 1h 15min 28s
    Wall time: 1h 8min 14s

The summary

```python
my_model.bootstrap_summary
```

      $\alpha$
      $\gamma$

      count
      50.000000
      50.000000

      mean
      0.103383
      0.005287

      std
      0.000092
      0.000007

      min
      0.103137
      0.005275

      25%
      0.103337
      0.005283

      50%
      0.103400
      0.005286

      75%
      0.103443
      0.005291

      max
      0.103549
      0.005304

The raw results

```python
my_model.bootstrap_raw
```

      $\alpha$
      $\gamma$

      0
      0.103280
      0.005294

      1
      0.103292
      0.005293

      2
      0.103409
      0.005285

      3
      0.103407
      0.005284

      4
      0.103543
      0.005276

      5
      0.103406
      0.005286

      6
      0.103409
      0.005286

      7
      0.103237
      0.005297

      8
      0.103427
      0.005283

      9
      0.103400
      0.005286

      10
      0.103342
      0.005291

      11
      0.103366
      0.005288

      12
      0.103288
      0.005294

      13
      0.103433
      0.005283

      14
      0.103401
      0.005285

      15
      0.103278
      0.005296

      16
      0.103269
      0.005295

      17
      0.103344
      0.005290

      18
      0.103363
      0.005288

      19
      0.103137
      0.005304

      20
      0.103376
      0.005288

      21
      0.103294
      0.005293

      22
      0.103353
      0.005288

      23
      0.103401
      0.005285

      24
      0.103420
      0.005284

      25
      0.103505
      0.005278

      26
      0.103506
      0.005276

      27
      0.103370
      0.005288

      28
      0.103173
      0.005302

      29
      0.103386
      0.005286

      30
      0.103446
      0.005284

      31
      0.103447
      0.005283

      32
      0.103339
      0.005290

      33
      0.103408
      0.005286

      34
      0.103342
      0.005291

      35
      0.103308
      0.005292

      36
      0.103364
      0.005288

      37
      0.103286
      0.005294

      38
      0.103267
      0.005296

      39
      0.103540
      0.005276

      40
      0.103463
      0.005280

      41
      0.103403
      0.005286

      42
      0.103460
      0.005282

      43
      0.103490
      0.005280

      44
      0.103549
      0.005275

      45
      0.103482
      0.005281

      46
      0.103494
      0.005278

      47
      0.103511
      0.005278

      48
      0.103336
      0.005290

      49
      0.103413
      0.005284

Visualisation

```python
my_model.plot_bootstrap()
```

### Only one observed variable

Let us suppose now that we only have data for $m$.

```python
df2 = df[df.columns[[0,1,2,5]]] # n and f, which we are ignoring, are columns 3 and 4 in the data table

df2.head()
```

      0
      1
      2
      5

      0
      1.0
      0.0
      0.020408
      0.000670

      1
      1.0
      0.0
      0.020408
      0.000000

      2
      1.0
      0.0
      0.020408
      0.001314

      3
      1.0
      0.0
      0.061224
      0.000000

      4
      1.0
      0.0
      0.061224
      0.000075

#### Constructing the `PDEmodel` object.

The inputs are the same as before, except for the data—now `df2` instead of `df`—and `obsidx`, which specifies that we only have data for the 3rd variable in the system. Starting from zero, the corresponding index is 2. Since we only have one observed variable, `obsidx` can be either a number or a list of one element.

```python
my_model2 = pde.PDEmodel(df2, TumourGrowth, [initial_N, initial_F2, initial_M],
                        bounds=[(0.05, 0.15), (0.002,0.01)], param_names=[r'$\alpha$', r'$\gamma$'],
                        nvars=3, ndims=2, nreplicates=3, obsidx=[2], outfunc=None)
```

#### Finding the best fit parameters.

```python
%%time
my_model2.fit()
```

       $\alpha$  $\gamma$
    0  0.098852  0.005657
    CPU times: user 1min 14s, sys: 2.89 s, total: 1min 17s
    Wall time: 1min 8s

```python
my_model2.best_params
```

      $\alpha$
      $\gamma$

      0
      0.098852
      0.005657

```python
my_model2.best_error
```

    1.0316723898609223e-06

#### Likelihood profiles

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model2.likelihood_profiles(npoints=25)
```

    HBox(children=(IntProgress(value=0, description='parameters', max=2, style=ProgressStyle(description_width='in…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    CPU times: user 49min 40s, sys: 3min 25s, total: 53min 5s
    Wall time: 42min 29s

The raw results

```python
my_model2.result_profiles
```

      parameter
      value
      error

      0
      $\alpha$
      0.050000
      0.000020

      1
      $\alpha$
      0.054167
      0.000017

      2
      $\alpha$
      0.058333
      0.000014

      3
      $\alpha$
      0.062500
      0.000011

      4
      $\alpha$
      0.066667
      0.000009

      5
      $\alpha$
      0.070833
      0.000007

      6
      $\alpha$
      0.075000
      0.000005

      7
      $\alpha$
      0.079167
      0.000004

      8
      $\alpha$
      0.083333
      0.000003

      9
      $\alpha$
      0.087500
      0.000002

      10
      $\alpha$
      0.091667
      0.000001

      11
      $\alpha$
      0.095833
      0.000001

      12
      $\alpha$
      0.100000
      0.000001

      13
      $\alpha$
      0.104167
      0.000001

      14
      $\alpha$
      0.108333
      0.000002

      15
      $\alpha$
      0.112500
      0.000002

      16
      $\alpha$
      0.116667
      0.000003

      17
      $\alpha$
      0.120833
      0.000004

      18
      $\alpha$
      0.125000
      0.000006

      19
      $\alpha$
      0.129167
      0.000008

      20
      $\alpha$
      0.133333
      0.000009

      21
      $\alpha$
      0.137500
      0.000011

      22
      $\alpha$
      0.141667
      0.000014

      23
      $\alpha$
      0.145833
      0.000016

      24
      $\alpha$
      0.150000
      0.000019

      25
      $\gamma$
      0.002000
      0.000010

      26
      $\gamma$
      0.002333
      0.000009

      27
      $\gamma$
      0.002667
      0.000007

      28
      $\gamma$
      0.003000
      0.000006

      29
      $\gamma$
      0.003333
      0.000005

      30
      $\gamma$
      0.003667
      0.000004

      31
      $\gamma$
      0.004000
      0.000003

      32
      $\gamma$
      0.004333
      0.000002

      33
      $\gamma$
      0.004667
      0.000002

      34
      $\gamma$
      0.005000
      0.000001

      35
      $\gamma$
      0.005333
      0.000001

      36
      $\gamma$
      0.005667
      0.000001

      37
      $\gamma$
      0.006000
      0.000001

      38
      $\gamma$
      0.006333
      0.000001

      39
      $\gamma$
      0.006667
      0.000002

      40
      $\gamma$
      0.007000
      0.000002

      41
      $\gamma$
      0.007333
      0.000002

      42
      $\gamma$
      0.007667
      0.000003

      43
      $\gamma$
      0.008000
      0.000004

      44
      $\gamma$
      0.008333
      0.000004

      45
      $\gamma$
      0.008667
      0.000005

      46
      $\gamma$
      0.009000
      0.000006

      47
      $\gamma$
      0.009333
      0.000006

      48
      $\gamma$
      0.009667
      0.000007

      49
      $\gamma$
      0.010000
      0.000008

Visualisation

```python
my_model2.plot_profiles()
```

#### Bootstrapping

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model2.bootstrap(nruns=50)
```

    HBox(children=(IntProgress(value=0, description='runs', max=50, style=ProgressStyle(description_width='initial…

            $\alpha$   $\gamma$
    count  50.000000  50.000000
    mean    0.098954   0.005656
    std     0.000135   0.000013
    min     0.098674   0.005623
    25%     0.098862   0.005648
    50%     0.098947   0.005656
    75%     0.099022   0.005664
    max     0.099387   0.005684
    CPU times: user 58min 36s, sys: 1min 58s, total: 1h 34s
    Wall time: 54min 28s

The summary

```python
my_model2.bootstrap_summary
```

      $\alpha$
      $\gamma$

      count
      50.000000
      50.000000

      mean
      0.098954
      0.005656

      std
      0.000135
      0.000013

      min
      0.098674
      0.005623

      25%
      0.098862
      0.005648

      50%
      0.098947
      0.005656

      75%
      0.099022
      0.005664

      max
      0.099387
      0.005684

The raw results

```python
my_model2.bootstrap_raw
```

      $\alpha$
      $\gamma$

      0
      0.099155
      0.005673

      1
      0.098928
      0.005642

      2
      0.098944
      0.005658

      3
      0.098741
      0.005661

      4
      0.098947
      0.005634

      5
      0.098973
      0.005646

      6
      0.099091
      0.005654

      7
      0.099073
      0.005669

      8
      0.099001
      0.005655

      9
      0.098858
      0.005656

      10
      0.098760
      0.005672

      11
      0.099255
      0.005677

      12
      0.098900
      0.005662

      13
      0.098931
      0.005683

      14
      0.098972
      0.005673

      15
      0.099387
      0.005684

      16
      0.098873
      0.005649

      17
      0.098946
      0.005649

      18
      0.098874
      0.005668

      19
      0.099117
      0.005667

      20
      0.098889
      0.005648

      21
      0.099122
      0.005667

      22
      0.098997
      0.005651

      23
      0.098755
      0.005644

      24
      0.099015
      0.005660

      25
      0.099030
      0.005654

      26
      0.098674
      0.005646

      27
      0.098807
      0.005647

      28
      0.098811
      0.005649

      29
      0.098977
      0.005665

      30
      0.099090
      0.005663

      31
      0.099024
      0.005646

      32
      0.098935
      0.005662

      33
      0.098819
      0.005639

      34
      0.098930
      0.005635

      35
      0.099055
      0.005658

      36
      0.099018
      0.005650

      37
      0.098837
      0.005623

      38
      0.098952
      0.005655

      39
      0.098732
      0.005668

      40
      0.098782
      0.005658

      41
      0.098947
      0.005636

      42
      0.099016
      0.005641

      43
      0.098851
      0.005662

      44
      0.099072
      0.005663

      45
      0.099076
      0.005636

      46
      0.098933
      0.005652

      47
      0.098964
      0.005650

      48
      0.098993
      0.005664

      49
      0.098853
      0.005665

Visualisation

```python
my_model2.plot_bootstrap()
```

### Only a function of the variables is observed

Finally, we consider the case when it's not the raw outputs of the system that are observed, but rather a function of them. In this case, let us assume that the observed quantity is

$$\mathcal{F}(n, f, m) = n^2 - m$$

```python
df3 = df[df.columns[[0,1,2]]] # we take only the space-time coordinates

# We add the new function
df3[r'$\mathcal{F}$'] = df['3']*df['3'] - df['5'] # n and m are, respectively, columns 3 and 5 in the data table

df3.head()
```

      0
      1
      2
      $\mathcal{F}$

      0
      1.0
      0.0
      0.020408
      -6.687299e-04

      1
      1.0
      0.0
      0.020408
      6.859981e-07

      2
      1.0
      0.0
      0.020408
      -1.314136e-03

      3
      1.0
      0.0
      0.061224
      1.122511e-06

      4
      1.0
      0.0
      0.061224
      -7.427586e-05

#### Constructing the `PDEmodel` object.

The inputs are the same as before, except for the data—now `df3`—and `outfunc`, which specifies the function to be applied to the outputs before computing the error. Note that, even if the output function uses only 2 of the variables, we need to pass the full output vector as an argument (see below). In this case, `obsidx` is ignored, but we set it to `None` anyway.

```python
def F(z):
    n, f, m = z

    return n**2-m
```

```python
my_model3 = pde.PDEmodel(df3, TumourGrowth, [initial_N, initial_F2, initial_M],
                        bounds=[(0.05, 0.15), (0.002,0.01)], param_names=[r'$\alpha$', r'$\gamma$'],
                        nvars=3, ndims=2, nreplicates=3, obsidx=None, outfunc=F)
```

#### Finding the best fit parameters.

```python
%%time
my_model3.fit()
```

       $\alpha$  $\gamma$
    0  0.099206  0.005681
    CPU times: user 1min 46s, sys: 1.7 s, total: 1min 47s
    Wall time: 1min 44s

```python
my_model3.best_params
```

      $\alpha$
      $\gamma$

      0
      0.099206
      0.005681

```python
my_model3.best_error
```

    1.106811988396949e-06

#### Likelihood profiles

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model3.likelihood_profiles(npoints=25)
```

    HBox(children=(IntProgress(value=0, description='parameters', max=2, style=ProgressStyle(description_width='in…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    HBox(children=(IntProgress(value=0, description='values within parameters', max=25, style=ProgressStyle(descri…

    CPU times: user 1h 5min, sys: 3min 13s, total: 1h 8min 13s
    Wall time: 58min 3s

The raw results

```python
my_model3.result_profiles
```

      parameter
      value
      error

      0
      $\alpha$
      0.050000
      0.000021

      1
      $\alpha$
      0.054167
      0.000018

      2
      $\alpha$
      0.058333
      0.000015

      3
      $\alpha$
      0.062500
      0.000012

      4
      $\alpha$
      0.066667
      0.000010

      5
      $\alpha$
      0.070833
      0.000008

      6
      $\alpha$
      0.075000
      0.000006

      7
      $\alpha$
      0.079167
      0.000004

      8
      $\alpha$
      0.083333
      0.000003

      9
      $\alpha$
      0.087500
      0.000002

      10
      $\alpha$
      0.091667
      0.000002

      11
      $\alpha$
      0.095833
      0.000001

      12
      $\alpha$
      0.100000
      0.000001

      13
      $\alpha$
      0.104167
      0.000001

      14
      $\alpha$
      0.108333
      0.000002

      15
      $\alpha$
      0.112500
      0.000002

      16
      $\alpha$
      0.116667
      0.000003

      17
      $\alpha$
      0.120833
      0.000004

      18
      $\alpha$
      0.125000
      0.000006

      19
      $\alpha$
      0.129167
      0.000008

      20
      $\alpha$
      0.133333
      0.000009

      21
      $\alpha$
      0.137500
      0.000012

      22
      $\alpha$
      0.141667
      0.000014

      23
      $\alpha$
      0.145833
      0.000016

      24
      $\alpha$
      0.150000
      0.000019

      25
      $\gamma$
      0.002000
      0.000010

      26
      $\gamma$
      0.002333
      0.000009

      27
      $\gamma$
      0.002667
      0.000007

      28
      $\gamma$
      0.003000
      0.000006

      29
      $\gamma$
      0.003333
      0.000005

      30
      $\gamma$
      0.003667
      0.000004

      31
      $\gamma$
      0.004000
      0.000003

      32
      $\gamma$
      0.004333
      0.000002

      33
      $\gamma$
      0.004667
      0.000002

      34
      $\gamma$
      0.005000
      0.000001

      35
      $\gamma$
      0.005333
      0.000001

      36
      $\gamma$
      0.005667
      0.000001

      37
      $\gamma$
      0.006000
      0.000001

      38
      $\gamma$
      0.006333
      0.000001

      39
      $\gamma$
      0.006667
      0.000002

      40
      $\gamma$
      0.007000
      0.000002

      41
      $\gamma$
      0.007333
      0.000002

      42
      $\gamma$
      0.007667
      0.000003

      43
      $\gamma$
      0.008000
      0.000003

      44
      $\gamma$
      0.008333
      0.000004

      45
      $\gamma$
      0.008667
      0.000005

      46
      $\gamma$
      0.009000
      0.000005

      47
      $\gamma$
      0.009333
      0.000006

      48
      $\gamma$
      0.009667
      0.000007

      49
      $\gamma$
      0.010000
      0.000008

Visualisation

```python
my_model3.plot_profiles()
```

#### Bootstrapping

**Note:** if you see a "widget not found" message, just ignore it; a progress bar will appear when you run the cell below.

```python
%%time
my_model3.bootstrap(nruns=50)
```

    HBox(children=(IntProgress(value=0, description='runs', max=50, style=ProgressStyle(description_width='initial…

            $\alpha$   $\gamma$
    count  50.000000  50.000000
    mean    0.099367   0.005695
    std     0.000141   0.000015
    min     0.099032   0.005660
    25%     0.099296   0.005686
    50%     0.099364   0.005694
    75%     0.099442   0.005702
    max     0.099672   0.005729
    CPU times: user 1h 6min 39s, sys: 2min 1s, total: 1h 8min 40s
    Wall time: 1h 2min 20s

The summary

```python
my_model3.bootstrap_summary
```

      $\alpha$
      $\gamma$

      count
      50.000000
      50.000000

      mean
      0.099367
      0.005695

      std
      0.000141
      0.000015

      min
      0.099032
      0.005660

      25%
      0.099296
      0.005686

      50%
      0.099364
      0.005694

      75%
      0.099442
      0.005702

      max
      0.099672
      0.005729

The raw results

```python
my_model3.bootstrap_raw
```

      $\alpha$
      $\gamma$

      0
      0.099379
      0.005704

      1
      0.099286
      0.005696

      2
      0.099366
      0.005700

      3
      0.099502
      0.005692

      4
      0.099267
      0.005714

      5
      0.099397
      0.005678

      6
      0.099320
      0.005691

      7
      0.099672
      0.005721

      8
      0.099243
      0.005691

      9
      0.099160
      0.005689

      10
      0.099472
      0.005683

      11
      0.099568
      0.005703

      12
      0.099392
      0.005662

      13
      0.099120
      0.005672

      14
      0.099416
      0.005718

      15
      0.099296
      0.005687

      16
      0.099379
      0.005684

      17
      0.099658
      0.005718

      18
      0.099202
      0.005660

      19
      0.099495
      0.005693

      20
      0.099236
      0.005683

      21
      0.099300
      0.005707

      22
      0.099240
      0.005695

      23
      0.099388
      0.005700

      24
      0.099355
      0.005689

      25
      0.099324
      0.005715

      26
      0.099469
      0.005696

      27
      0.099325
      0.005701

      28
      0.099379
      0.005673

      29
      0.099399
      0.005698

      30
      0.099519
      0.005683

      31
      0.099628
      0.005729

      32
      0.099343
      0.005686

      33
      0.099296
      0.005685

      34
      0.099395
      0.005699

      35
      0.099032
      0.005688

      36
      0.099550
      0.005719

      37
      0.099552
      0.005701

      38
      0.099421
      0.005690

      39
      0.099354
      0.005702

      40
      0.099364
      0.005689

      41
      0.099618
      0.005712

      42
      0.099448
      0.005700

      43
      0.099142
      0.005697

      44
      0.099297
      0.005716

      45
      0.099169
      0.005681

      46
      0.099364
      0.005692

      47
      0.099315
      0.005677

      48
      0.099320
      0.005695

      49
      0.099194
      0.005688

Visualisation

```python
my_model3.plot_bootstrap()
```


# Multigrid examples

```python
%matplotlib inline
import matplotlib.pyplot as plt
```

```python
from __future__ import print_function

import numpy as np

import mesh.boundary as bnd
import mesh.patch as patch
import multigrid.MG as MG
```

## Constant-coefficent Poisson equation

We want to solve

$$\phi_{xx} + \phi_{yy} = -2[(1-6x^2)y^2(1-y^2) + (1-6y^2)x^2(1-x^2)]$$

on

$$[0,1]\times [0,1]$$

with homogeneous Dirichlet boundary conditions (this example comes from "A Multigrid Tutorial").

This has the analytic solution
$$u(x,y) = (x^2 - x^4)(y^4 - y^2)$$

We start by setting up a multigrid object--this needs to know the number of zones our problem is defined on

```python
nx = ny = 256
mg = MG.CellCenterMG2d(nx, ny,
                       xl_BC_type="dirichlet", xr_BC_type="dirichlet",
                       yl_BC_type="dirichlet", yr_BC_type="dirichlet", verbose=1)
```

    cc data: nx = 2, ny = 2, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 4, ny = 4, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 8, ny = 8, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 16, ny = 16, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 32, ny = 32, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 64, ny = 64, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 128, ny = 128, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

    cc data: nx = 256, ny = 256, ng = 1
             nvars = 3
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet

Next, we initialize the RHS.  To make life easier, the `CellCenterMG2d` object has the coordinates of the solution grid (including ghost cells) as `mg.x2d` and `mg.y2d` (these are two-dimensional arrays).

```python
def rhs(x, y):
    return -2.0*((1.0-6.0*x**2)*y**2*(1.0-y**2) + (1.0-6.0*y**2)*x**2*(1.0-x**2))

mg.init_RHS(rhs(mg.x2d, mg.y2d))
```

    Source norm =  1.097515813669473

The last setup step is to initialize the solution--this is the starting point for the solve.  Usually we just want to start with all zeros, so we use the `init_zeros()` method

```python
mg.init_zeros()
```

we can now solve -- there are actually two different techniques we can do here.  We can just do pure smoothing on the solution grid using `mg.smooth(mg.nlevels-1, N)`, where `N` is the number of smoothing iterations.  To get the solution `N` will need to be large and this will take a long time.

Multigrid accelerates the smoothing.  We can do a V-cycle multigrid solution using `mg.solve()`

```python
mg.solve()
```

    source norm =  1.097515813669473
    <<< beginning V-cycle (cycle 1) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.097515813669473
      after G-S, residual L2: 1.502308451578657

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.0616243965458263
      after G-S, residual L2: 1.4321452257629033

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.011366277976364
      after G-S, residual L2: 1.281872470375375

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.903531158162907
      after G-S, residual L2: 0.9607576999783505

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.6736112182020367
      after G-S, residual L2: 0.4439774050299674

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.30721142286171554
      after G-S, residual L2: 0.0727215591269748

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.04841813458618458
      after G-S, residual L2: 3.9610700301811246e-05

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 3.925006722484123e-05
      after G-S, residual L2: 1.0370099820862674e-09

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.07010129273961899
      after G-S, residual L2: 0.0008815704830693547

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.4307377377402105
      after G-S, residual L2: 0.007174899576794818

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.911086486792154
      after G-S, residual L2: 0.01618756602227813

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.1945438349788615
      after G-S, residual L2: 0.022021327892004925

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.313456560108626
      after G-S, residual L2: 0.02518650395173617

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.3618314516335004
      after G-S, residual L2: 0.026870007568672097

    cycle 1: relative err = 0.999999999999964, residual err = 0.02448256984911586

    <<< beginning V-cycle (cycle 2) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 0.026870007568672097
      after G-S, residual L2: 0.025790216249923552

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.018218080204017304
      after G-S, residual L2: 0.023654310121915274

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.01669077991582338
      after G-S, residual L2: 0.01977335201785163

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.013922595404814862
      after G-S, residual L2: 0.013577568890182053

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.009518306167970536
      after G-S, residual L2: 0.006115159484497302

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.004244630812032651
      after G-S, residual L2: 0.0010674120586864006

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.0007108144252738053
      after G-S, residual L2: 5.818246254772703e-07

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 5.765281065294632e-07
      after G-S, residual L2: 1.5231212503339452e-11

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.0010291471590693868
      after G-S, residual L2: 1.2950948742201083e-05

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.006239446983842889
      after G-S, residual L2: 0.00010483463130232172

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.014573363314854
      after G-S, residual L2: 0.00026233988398787004

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.021564270263952755
      after G-S, residual L2: 0.0003944827058086955

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.02579092712136628
      after G-S, residual L2: 0.00048636495715121916

      level: 7, grid: 256 x 256
      before G-S, residual L2: 0.028051324215592862
      after G-S, residual L2: 0.0005440874957950154

    cycle 2: relative err = 13.739483825281054, residual err = 0.0004957445615074047

    <<< beginning V-cycle (cycle 3) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 0.0005440874957950154
      after G-S, residual L2: 0.0005095844930046698

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.0003597879816772893
      after G-S, residual L2: 0.00044648485218937167

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.0003147892995472901
      after G-S, residual L2: 0.0003492541721056348

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.0002457276904804801
      after G-S, residual L2: 0.00022232862524233384

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.0001558932199490972
      after G-S, residual L2: 9.511093023364078e-05

      level: 2, grid: 8 x 8
      before G-S, residual L2: 6.616899520585456e-05
      after G-S, residual L2: 1.711006102346096e-05

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.139522901981679e-05
      after G-S, residual L2: 9.33004809910226e-09

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 9.245125097272049e-09
      after G-S, residual L2: 2.442311694447821e-13

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.64991725637487e-05
      after G-S, residual L2: 2.0771258971860784e-07

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.00010097720436460624
      after G-S, residual L2: 1.7241727900979902e-06

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.0002575410544503153
      after G-S, residual L2: 4.766282851613449e-06

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.00041133882050328275
      after G-S, residual L2: 7.600616845344458e-06

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.0005232809692242086
      after G-S, residual L2: 9.860758095018993e-06

      level: 7, grid: 256 x 256
      before G-S, residual L2: 0.0005945070122423073
      after G-S, residual L2: 1.1466134915427874e-05

    cycle 3: relative err = 34.347638624909216, residual err = 1.0447352805871284e-05

    <<< beginning V-cycle (cycle 4) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.1466134915427874e-05
      after G-S, residual L2: 1.054466722279011e-05

      level: 6, grid: 128 x 128
      before G-S, residual L2: 7.442814693866286e-06
      after G-S, residual L2: 8.955050475722099e-06

      level: 5, grid: 64 x 64
      before G-S, residual L2: 6.311313968968047e-06
      after G-S, residual L2: 6.734553609148436e-06

      level: 4, grid: 32 x 32
      before G-S, residual L2: 4.737984987500691e-06
      after G-S, residual L2: 4.091799997658277e-06

      level: 3, grid: 16 x 16
      before G-S, residual L2: 2.871028473858937e-06
      after G-S, residual L2: 1.6319551993366253e-06

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.1372178077508109e-06
      after G-S, residual L2: 2.961040430099916e-07

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.9721864323458624e-07
      after G-S, residual L2: 1.61503943872384e-10

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.6003411195777404e-10
      after G-S, residual L2: 4.2274326344473505e-15

      level: 2, grid: 8 x 8
      before G-S, residual L2: 2.855691101825338e-07
      after G-S, residual L2: 3.5961118754371857e-09

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.7893831203170535e-06
      after G-S, residual L2: 3.1136282101831173e-08

      level: 4, grid: 32 x 32
      before G-S, residual L2: 4.97129807196115e-06
      after G-S, residual L2: 9.544819739422644e-08

      level: 5, grid: 64 x 64
      before G-S, residual L2: 8.281644276572538e-06
      after G-S, residual L2: 1.56637783149839e-07

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.0888850082357996e-05
      after G-S, residual L2: 2.0777271327080248e-07

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.2717522622400765e-05
      after G-S, residual L2: 2.464531349025277e-07

    cycle 4: relative err = 0.17409776671446628, residual err = 2.24555429482631e-07

    <<< beginning V-cycle (cycle 5) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 2.464531349025277e-07
      after G-S, residual L2: 2.2491138140311698e-07

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.5874562191875262e-07
      after G-S, residual L2: 1.886249099391391e-07

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.3294481979637655e-07
      after G-S, residual L2: 1.397710191717015e-07

      level: 4, grid: 32 x 32
      before G-S, residual L2: 9.836928907527788e-08
      after G-S, residual L2: 8.269030961692836e-08

      level: 3, grid: 16 x 16
      before G-S, residual L2: 5.8062531341283565e-08
      after G-S, residual L2: 3.034725896415429e-08

      level: 2, grid: 8 x 8
      before G-S, residual L2: 2.116912379336852e-08
      after G-S, residual L2: 5.467519592468213e-09

      level: 1, grid: 4 x 4
      before G-S, residual L2: 3.6418116003284676e-09
      after G-S, residual L2: 2.982625229812215e-12

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 2.955484162036181e-12
      after G-S, residual L2: 7.806739482450516e-17

      level: 2, grid: 8 x 8
      before G-S, residual L2: 5.273610709946236e-09
      after G-S, residual L2: 6.642323465658688e-11

      level: 3, grid: 16 x 16
      before G-S, residual L2: 3.4146989205844565e-08
      after G-S, residual L2: 6.052228076583688e-10

      level: 4, grid: 32 x 32
      before G-S, residual L2: 1.031248597196911e-07
      after G-S, residual L2: 2.0541497445308587e-09

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.7585349306604133e-07
      after G-S, residual L2: 3.421022608879089e-09

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.3383756442516674e-07
      after G-S, residual L2: 4.552170797983864e-09

      level: 7, grid: 256 x 256
      before G-S, residual L2: 2.7592842790687426e-07
      after G-S, residual L2: 5.41488950707315e-09

    cycle 5: relative err = 0.005391244339065405, residual err = 4.933769007818501e-09

    <<< beginning V-cycle (cycle 6) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 5.41488950707315e-09
      after G-S, residual L2: 4.948141362729419e-09

      level: 6, grid: 128 x 128
      before G-S, residual L2: 3.4929583962703016e-09
      after G-S, residual L2: 4.154445183511443e-09

      level: 5, grid: 64 x 64
      before G-S, residual L2: 2.9288841397931397e-09
      after G-S, residual L2: 3.074779198797186e-09

      level: 4, grid: 32 x 32
      before G-S, residual L2: 2.164991235492634e-09
      after G-S, residual L2: 1.788028730183651e-09

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.2562223343389894e-09
      after G-S, residual L2: 6.021983813990021e-10

      level: 2, grid: 8 x 8
      before G-S, residual L2: 4.2028073688787063e-10
      after G-S, residual L2: 1.0655724637281067e-10

      level: 1, grid: 4 x 4
      before G-S, residual L2: 7.097871736854444e-11
      after G-S, residual L2: 5.813506543301849e-14

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 5.760611936011378e-14
      after G-S, residual L2: 1.521555112430923e-18

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.027891920456506e-10
      after G-S, residual L2: 1.294879454701896e-12

      level: 3, grid: 16 x 16
      before G-S, residual L2: 6.914011940773812e-10
      after G-S, residual L2: 1.2453691230551983e-11

      level: 4, grid: 32 x 32
      before G-S, residual L2: 2.2570491487662195e-09
      after G-S, residual L2: 4.639035392364569e-11

      level: 5, grid: 64 x 64
      before G-S, residual L2: 3.908967396962745e-09
      after G-S, residual L2: 7.803740782474827e-11

      level: 6, grid: 128 x 128
      before G-S, residual L2: 5.196394306272565e-09
      after G-S, residual L2: 1.033274523100204e-10

      level: 7, grid: 256 x 256
      before G-S, residual L2: 6.117636729623554e-09
      after G-S, residual L2: 1.2199402602477584e-10

    cycle 6: relative err = 7.51413991329132e-05, residual err = 1.111546863428753e-10

    <<< beginning V-cycle (cycle 7) >>>

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.2199402602477584e-10
      after G-S, residual L2: 1.121992266879251e-10

      level: 6, grid: 128 x 128
      before G-S, residual L2: 7.921861122082639e-11
      after G-S, residual L2: 9.493449600138316e-11

      level: 5, grid: 64 x 64
      before G-S, residual L2: 6.694993398453784e-11
      after G-S, residual L2: 7.050995614737483e-11

      level: 4, grid: 32 x 32
      before G-S, residual L2: 4.9666563586565975e-11
      after G-S, residual L2: 4.045094776680348e-11

      level: 3, grid: 16 x 16
      before G-S, residual L2: 2.843147343834713e-11
      after G-S, residual L2: 1.2576313722677801e-11

      level: 2, grid: 8 x 8
      before G-S, residual L2: 8.777954081387978e-12
      after G-S, residual L2: 2.170559196862902e-12

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.445876195415056e-12
      after G-S, residual L2: 1.1842925278593641e-15

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.1735184729034125e-15
      after G-S, residual L2: 3.0994757710835167e-20

      level: 2, grid: 8 x 8
      before G-S, residual L2: 2.094012660676073e-12
      after G-S, residual L2: 2.6382579574150587e-14

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.466147487151147e-11
      after G-S, residual L2: 2.6760553592700965e-13

      level: 4, grid: 32 x 32
      before G-S, residual L2: 5.130705216489902e-11
      after G-S, residual L2: 1.0810419626613159e-12

      level: 5, grid: 64 x 64
      before G-S, residual L2: 9.001551103280705e-11
      after G-S, residual L2: 1.8342879121275396e-12

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.1914921193827463e-10
      after G-S, residual L2: 2.4124327865487605e-12

      level: 7, grid: 256 x 256
      before G-S, residual L2: 1.3907209384461257e-10
      after G-S, residual L2: 2.8429898342353533e-12

    cycle 7: relative err = 7.062255558417692e-07, residual err = 2.590386214782638e-12

We can access the solution on the finest grid using `get_solution()`

```python
phi = mg.get_solution()
```

```python
plt.imshow(np.transpose(phi.v()), origin="lower")
```

we can also get the gradient of the solution

```python
gx, gy = mg.get_solution_gradient()
```

```python
plt.subplot(121)
plt.imshow(np.transpose(gx.v()), origin="lower")
plt.subplot(122)
plt.imshow(np.transpose(gy.v()), origin="lower")
```

## General linear elliptic equation

The `GeneralMG2d` class implements support for a general elliptic equation of the form:
$$\alpha \phi + \nabla \cdot (\beta \nabla \phi) + \gamma \cdot \nabla \phi = f$$

with inhomogeneous boundary condtions.

It subclasses the `CellCenterMG2d` class, and the basic interface is the same

We will solve the above with
\begin{align}
\alpha &= 10 \\
\beta &= xy + 1 \\
\gamma &= \hat{x} + \hat{y}
\end{align}
and
\begin{align}
f = &-\frac{\pi}{2}(x + 1)\sin\left(\frac{\pi y}{2}\right) \cos\left(\frac{\pi x}{2}\right ) \\
    &-\frac{\pi}{2}(y + 1)\sin\left(\frac{\pi x}{2}\right) \cos\left(\frac{\pi y}{2}\right ) \\
    &+\left(\frac{-\pi^2 (xy+1)}{2} + 10\right) \cos\left(\frac{\pi x}{2}\right) \cos\left(\frac{\pi y}{2}\right)
\end{align}
on $[0, 1] \times [0,1]$ with boundary conditions:
\begin{align}
\phi(x=0) &= \cos(\pi y/2) \\
\phi(x=1) &= 0 \\
\phi(y=0) &= \cos(\pi x/2) \\
\phi(y=1) &= 0
\end{align}

This has the exact solution:
$$\phi = \cos(\pi x/2) \cos(\pi y/2)$$

```python
import multigrid.general_MG as gMG
```

For reference, we'll define a function providing the analytic solution

```python
def true(x,y):
    return np.cos(np.pi*x/2.0)*np.cos(np.pi*y/2.0)
```

Now the coefficents--note that since $\gamma$ is a vector, we have a different function for each component

```python
def alpha(x,y):
    return 10.0*np.ones_like(x)

def beta(x,y):
    return x*y + 1.0

def gamma_x(x,y):
    return np.ones_like(x)

def gamma_y(x,y):
    return np.ones_like(x)
```

and the righthand side function

```python
def f(x,y):
    return -0.5*np.pi*(x + 1.0)*np.sin(np.pi*y/2.0)*np.cos(np.pi*x/2.0) - \
            0.5*np.pi*(y + 1.0)*np.sin(np.pi*x/2.0)*np.cos(np.pi*y/2.0) + \
            (-np.pi**2*(x*y+1.0)/2.0 + 10.0)*np.cos(np.pi*x/2.0)*np.cos(np.pi*y/2.0)
```

Our inhomogeneous boundary conditions require a function that can be evaluated on the boundary to give the value

```python
def xl_func(y):
    return np.cos(np.pi*y/2.0)

def yl_func(x):
    return np.cos(np.pi*x/2.0)
```

Now we can setup our grid object and the coefficients, which are stored as a `CellCenter2d` object.  Note, the coefficients do not need to have the same boundary conditions as $\phi$ (and for real problems, they may not).  The one that matters the most is $\beta$, since that will need to be averaged to the edges of the domain, so the boundary conditions on the coefficients are important.

Here we use Neumann boundary conditions

```python
import mesh.patch as patch

nx = ny = 128

g = patch.Grid2d(nx, ny, ng=1)
d = patch.CellCenterData2d(g)

bc_c = bnd.BC(xlb="neumann", xrb="neumann",
              ylb="neumann", yrb="neumann")

d.register_var("alpha", bc_c)
d.register_var("beta", bc_c)
d.register_var("gamma_x", bc_c)
d.register_var("gamma_y", bc_c)
d.create()

a = d.get_var("alpha")
a[:,:] = alpha(g.x2d, g.y2d)

b = d.get_var("beta")
b[:,:] = beta(g.x2d, g.y2d)

gx = d.get_var("gamma_x")
gx[:,:] = gamma_x(g.x2d, g.y2d)

gy = d.get_var("gamma_y")
gy[:,:] = gamma_y(g.x2d, g.y2d)
```

Now we can setup the multigrid object

```python
a = gMG.GeneralMG2d(nx, ny,
                    xl_BC_type="dirichlet", yl_BC_type="dirichlet",
                    xr_BC_type="dirichlet", yr_BC_type="dirichlet",
                    xl_BC=xl_func,
                    yl_BC=yl_func,
                    coeffs=d,
                    verbose=1, vis=0, true_function=true)
```

    cc data: nx = 2, ny = 2, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 4, ny = 4, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 8, ny = 8, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 16, ny = 16, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 32, ny = 32, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 64, ny = 64, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

    cc data: nx = 128, ny = 128, ng = 1
             nvars = 7
             variables:
                   v: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   f: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
                   r: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: dirichlet    +x: dirichlet    -y: dirichlet    +y: dirichlet
               alpha: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
                beta: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_x: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann
             gamma_y: min:    0.0000000000    max:    0.0000000000
                      BCs: -x: neumann      +x: neumann      -y: neumann      +y: neumann

just as before, we specify the righthand side and initialize the solution

```python
a.init_zeros()
a.init_RHS(f(a.x2d, a.y2d))
```

    Source norm =  1.775181492337501

and we can solve it

```python
a.solve(rtol=1.e-10)
```

    source norm =  1.775181492337501
    <<< beginning V-cycle (cycle 1) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.775181492337501
      after G-S, residual L2: 188.9332667507471

      level: 5, grid: 64 x 64
      before G-S, residual L2: 129.93801550392874
      after G-S, residual L2: 56.28708770794368

      level: 4, grid: 32 x 32
      before G-S, residual L2: 38.88692621665778
      after G-S, residual L2: 18.722754099081875

      level: 3, grid: 16 x 16
      before G-S, residual L2: 12.92606814051491
      after G-S, residual L2: 6.7418584016115615

      level: 2, grid: 8 x 8
      before G-S, residual L2: 4.646478379380238
      after G-S, residual L2: 2.0651261541465855

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.3745334259197386
      after G-S, residual L2: 0.02244519721859255

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.03125252087247784
      after G-S, residual L2: 8.232822131629766e-05

      level: 2, grid: 8 x 8
      before G-S, residual L2: 2.8059768631102897
      after G-S, residual L2: 0.07481536016729919

      level: 3, grid: 16 x 16
      before G-S, residual L2: 8.77240243659538
      after G-S, residual L2: 0.2436194269452686

      level: 4, grid: 32 x 32
      before G-S, residual L2: 19.59101132435104
      after G-S, residual L2: 0.5448263647958954

      level: 5, grid: 64 x 64
      before G-S, residual L2: 50.4641088994847
      after G-S, residual L2: 1.3597629173942345

      level: 6, grid: 128 x 128
      before G-S, residual L2: 160.2131163846867
      after G-S, residual L2: 4.125142056231144

    cycle 1: relative err = 0.9999999999999981, residual err = 2.323786088373021

    <<< beginning V-cycle (cycle 2) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 4.125142056231144
      after G-S, residual L2: 2.4247311846143984

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.6915411385849388
      after G-S, residual L2: 1.048624109440286

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.728341635357186
      after G-S, residual L2: 0.4554818109365305

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.3165327512850202
      after G-S, residual L2: 0.22128563126748022

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.15332496186655523
      after G-S, residual L2: 0.07471968817844267

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.04974939187294444
      after G-S, residual L2: 0.000813357286041041

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.0011325179143730278
      after G-S, residual L2: 2.9833778391774223e-06

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.10152627387884025
      after G-S, residual L2: 0.00270070470024105

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.2981467241559525
      after G-S, residual L2: 0.008199107952269268

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.5218848114624626
      after G-S, residual L2: 0.014956130961989951

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.9910630869231989
      after G-S, residual L2: 0.028422939317571477

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.0441877458177586
      after G-S, residual L2: 0.05829382601881069

    cycle 2: relative err = 0.036315310129800826, residual err = 0.03283823443993396

    <<< beginning V-cycle (cycle 3) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.05829382601881069
      after G-S, residual L2: 0.04172011870726864

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.029246699093099682
      after G-S, residual L2: 0.02335632639759113

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.01630629679281779
      after G-S, residual L2: 0.012906629461195187

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.009011110787953677
      after G-S, residual L2: 0.00731526293890866

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.005081499522859446
      after G-S, residual L2: 0.0025625265171556363

      level: 1, grid: 4 x 4
      before G-S, residual L2: 0.0017064130732666084
      after G-S, residual L2: 2.7912387046731846e-05

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 3.88652692543315e-05
      after G-S, residual L2: 1.0238217009469722e-07

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.0034819145217790757
      after G-S, residual L2: 9.252096659805304e-05

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.010064990348703503
      after G-S, residual L2: 0.000274405441825591

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.016032310448839227
      after G-S, residual L2: 0.0004558226543272719

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.02430374388018733
      after G-S, residual L2: 0.0007098551729200968

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.03777531891587048
      after G-S, residual L2: 0.001103512282001738

    cycle 3: relative err = 0.0012532978372415558, residual err = 0.0006216334987521017

    <<< beginning V-cycle (cycle 4) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.001103512282001738
      after G-S, residual L2: 0.0008898317346982837

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.0006257398720757915
      after G-S, residual L2: 0.0006077401190832001

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.00042604165447805513
      after G-S, residual L2: 0.0003976740182571413

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.0002784624522915077
      after G-S, residual L2: 0.00024268300992448264

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.0001688184030128213
      after G-S, residual L2: 8.63435240004183e-05

      level: 1, grid: 4 x 4
      before G-S, residual L2: 5.750132804421135e-05
      after G-S, residual L2: 9.407985171394705e-07

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.309971480329214e-06
      after G-S, residual L2: 3.4508339509814223e-09

      level: 2, grid: 8 x 8
      before G-S, residual L2: 0.0001173242104275028
      after G-S, residual L2: 3.115753146780207e-06

      level: 3, grid: 16 x 16
      before G-S, residual L2: 0.0003385086711958763
      after G-S, residual L2: 9.177601888021123e-06

      level: 4, grid: 32 x 32
      before G-S, residual L2: 0.0005249527904445416
      after G-S, residual L2: 1.4651643231018942e-05

      level: 5, grid: 64 x 64
      before G-S, residual L2: 0.0007080871923403828
      after G-S, residual L2: 2.0290645679962866e-05

      level: 6, grid: 128 x 128
      before G-S, residual L2: 0.0009185166830467631
      after G-S, residual L2: 2.657030046653513e-05

    cycle 4: relative err = 4.257466296364851e-05, residual err = 1.4967652930826935e-05

    <<< beginning V-cycle (cycle 5) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.657030046653513e-05
      after G-S, residual L2: 2.3098223935313785e-05

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.62748573956126e-05
      after G-S, residual L2: 1.790614264207289e-05

      level: 4, grid: 32 x 32
      before G-S, residual L2: 1.258588239889598e-05
      after G-S, residual L2: 1.2880701434507449e-05

      level: 3, grid: 16 x 16
      before G-S, residual L2: 9.035061893240921e-06
      after G-S, residual L2: 8.103003189063974e-06

      level: 2, grid: 8 x 8
      before G-S, residual L2: 5.641504288200945e-06
      after G-S, residual L2: 2.901212906819645e-06

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.9321695178564805e-06
      after G-S, residual L2: 3.161675602297454e-08

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 4.402332099930838e-08
      after G-S, residual L2: 1.1596974315664115e-10

      level: 2, grid: 8 x 8
      before G-S, residual L2: 3.9422658752906826e-06
      after G-S, residual L2: 1.0466257646978307e-07

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.1405869021996882e-05
      after G-S, residual L2: 3.081954658995012e-07

      level: 4, grid: 32 x 32
      before G-S, residual L2: 1.769602521372454e-05
      after G-S, residual L2: 4.853326075346603e-07

      level: 5, grid: 64 x 64
      before G-S, residual L2: 2.2817221850081978e-05
      after G-S, residual L2: 6.339093027063977e-07

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.7204506619363593e-05
      after G-S, residual L2: 7.617366608567251e-07

    cycle 5: relative err = 1.437223355768636e-06, residual err = 4.2910353907176844e-07

    <<< beginning V-cycle (cycle 6) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 7.617366608567251e-07
      after G-S, residual L2: 6.887955156426563e-07

      level: 5, grid: 64 x 64
      before G-S, residual L2: 4.858303576730231e-07
      after G-S, residual L2: 5.698844687563798e-07

      level: 4, grid: 32 x 32
      before G-S, residual L2: 4.0114485957443323e-07
      after G-S, residual L2: 4.288730517202583e-07

      level: 3, grid: 16 x 16
      before G-S, residual L2: 3.011320287772163e-07
      after G-S, residual L2: 2.722913600393885e-07

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.8967555906447816e-07
      after G-S, residual L2: 9.770491560795584e-08

      level: 1, grid: 4 x 4
      before G-S, residual L2: 6.507167362727163e-08
      after G-S, residual L2: 1.0648579124114833e-09

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.4827137305196198e-09
      after G-S, residual L2: 3.905880555246777e-12

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.327670548521078e-07
      after G-S, residual L2: 3.5242457966015008e-09

      level: 3, grid: 16 x 16
      before G-S, residual L2: 3.856314492307069e-07
      after G-S, residual L2: 1.0398885089175414e-08

      level: 4, grid: 32 x 32
      before G-S, residual L2: 6.038836851891428e-07
      after G-S, residual L2: 1.6338312488394662e-08

      level: 5, grid: 64 x 64
      before G-S, residual L2: 7.682416354755789e-07
      after G-S, residual L2: 2.077211623429353e-08

      level: 6, grid: 128 x 128
      before G-S, residual L2: 8.865085868685839e-07
      after G-S, residual L2: 2.4019193506676187e-08

    cycle 6: relative err = 4.849259894834445e-08, residual err = 1.3530556515124825e-08

    <<< beginning V-cycle (cycle 7) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.4019193506676187e-08
      after G-S, residual L2: 2.2125281372771198e-08

      level: 5, grid: 64 x 64
      before G-S, residual L2: 1.561381038807939e-08
      after G-S, residual L2: 1.886960673167899e-08

      level: 4, grid: 32 x 32
      before G-S, residual L2: 1.329268816246029e-08
      after G-S, residual L2: 1.4485741753521445e-08

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.0177211567137172e-08
      after G-S, residual L2: 9.198083184898649e-09

      level: 2, grid: 8 x 8
      before G-S, residual L2: 6.409466908197649e-09
      after G-S, residual L2: 3.3018376959918963e-09

      level: 1, grid: 4 x 4
      before G-S, residual L2: 2.199060578574703e-09
      after G-S, residual L2: 3.598749904926513e-11

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 5.0109192227870255e-11
      after G-S, residual L2: 1.3200150083511782e-13

      level: 2, grid: 8 x 8
      before G-S, residual L2: 4.48679191788079e-09
      after G-S, residual L2: 1.1908944666026909e-10

      level: 3, grid: 16 x 16
      before G-S, residual L2: 1.308116183600633e-08
      after G-S, residual L2: 3.5229822636126385e-10

      level: 4, grid: 32 x 32
      before G-S, residual L2: 2.0705036251434032e-08
      after G-S, residual L2: 5.546643278736882e-10

      level: 5, grid: 64 x 64
      before G-S, residual L2: 2.628082249674873e-08
      after G-S, residual L2: 6.964954416636588e-10

      level: 6, grid: 128 x 128
      before G-S, residual L2: 2.99444121990791e-08
      after G-S, residual L2: 7.914127842417476e-10

    cycle 7: relative err = 1.6392149576904378e-09, residual err = 4.458207725000789e-10

    <<< beginning V-cycle (cycle 8) >>>

      level: 6, grid: 128 x 128
      before G-S, residual L2: 7.914127842417476e-10
      after G-S, residual L2: 7.355859728039993e-10

      level: 5, grid: 64 x 64
      before G-S, residual L2: 5.192197657358953e-10
      after G-S, residual L2: 6.364658784609479e-10

      level: 4, grid: 32 x 32
      before G-S, residual L2: 4.485501516615104e-10
      after G-S, residual L2: 4.92822276848544e-10

      level: 3, grid: 16 x 16
      before G-S, residual L2: 3.463701923819941e-10
      after G-S, residual L2: 3.1194086454251064e-10

      level: 2, grid: 8 x 8
      before G-S, residual L2: 2.1741762390347935e-10
      after G-S, residual L2: 1.1194487678586295e-10

      level: 1, grid: 4 x 4
      before G-S, residual L2: 7.455716542482062e-11
      after G-S, residual L2: 1.2201470093856338e-12

      bottom solve:
      level: 0, grid: 2 x 2

      level: 1, grid: 4 x 4
      before G-S, residual L2: 1.6989396365976684e-12
      after G-S, residual L2: 4.475476506307932e-15

      level: 2, grid: 8 x 8
      before G-S, residual L2: 1.5212108616548206e-10
      after G-S, residual L2: 4.037425067492746e-12

      level: 3, grid: 16 x 16
      before G-S, residual L2: 4.449137567321805e-10
      after G-S, residual L2: 1.1972449146203713e-11

      level: 4, grid: 32 x 32
      before G-S, residual L2: 7.109768080523844e-10
      after G-S, residual L2: 1.8912282007395565e-11

      level: 5, grid: 64 x 64
      before G-S, residual L2: 9.034003713243436e-10
      after G-S, residual L2: 2.3606440301264504e-11

      level: 6, grid: 128 x 128
      before G-S, residual L2: 1.0238015900738368e-09
      after G-S, residual L2: 2.6756968686627095e-11

    cycle 8: relative err = 5.555097426033948e-11, residual err = 1.5072807373286882e-11

We can compare to the true solution

```python
v = a.get_solution()
b = true(a.x2d, a.y2d)
e = v - b
```

The norm of the error is

```python
print(f"{e.norm():20.10g}")
```

         1.671934405e-05

```python

```