# Flow driven by an oscillating solid cylinder and bounded by a stationary outer hollow cylinder
\begin{equation}\label{p1introhpa} \Psi _{\eta \eta \tau }^{\mathrm{Sym}}  = \frac{1}{{R_s }}\Psi^{\mathrm{Sym}}_{\eta \eta \eta \eta }  + \Psi^{\mathrm{Sym}}_{\eta \eta \eta } \Psi _\theta^{\mathrm{Sym}}   - \Psi^{\mathrm{Sym}}_{\eta \eta \theta } \Psi^{\mathrm{Sym}}_\eta, \\
\quad \Psi^{\mathrm{Sym}}(\eta= 0) = 0,\quad \Psi^{\mathrm{Sym}}_{\eta\eta}(\eta= 0) = 0, \qquad \Psi^{\mathrm{Sym}}(\eta= 1) = 0, \quad \Psi^{\mathrm{Sym}}_\eta(\eta= 1) = - \mu sin\theta, \qquad
0 \le \eta \le1, \quad 0 \le \theta \le \pi
\end{equation}

## Calculating $\Psi(\theta,\eta)$ the steady state solution $\theta$-marching
The governing equation is solved using:

* Half domain $\eta$-domain $0 \le \eta \le 1$
* $\theta$ marching with some of the nonlinear terms being explicit
* At each $\theta$-step the code can optionally repeat the calculation for $\Psi^{j+1}$ by recalculating nonlinear terms implicitly, the number of iterations is controlled by the variable $K$

## Summary of results
$\theta$-marching with second order scheme seems to produce the biggest range of solutions, with $N=256$ and $M=128$ we can get a full $\theta$.
The numerical soltion is only smooth for  $0\leq\theta\leq\pi$ for very small reynolds numbers $-1< R_s< 2.0$, at higher Reynolds numbers the numerical errors accumulated as $\theta$ increases from zero and seem to be related to the choice of the meshsize $\Delta\theta$. When $\Delta\theta$ is *increased* the errors seem to vanish.

The solution generated by $\theta$-marching still matches the time marched solution near $\theta = 0$ upto  around $R_s = 45$ with very strong agreement with the 1st Order winding timemarched solution and less so with the second order winding scheme.

## Small $R_s$ series solution

```julia
using DifferentialEquations
using Plots
using BenchmarkTools
using DelimitedFiles
using LaTeXStrings
using SparseArrays
using LinearAlgebra
using StaticArrays
using Crayons.Box
using Printf
using Arpack
using IterativeSolvers
using IncompleteLU
```

```julia
function ψ_ηηηη(n ::Int64, m:: Int64, ψ::Matrix{Float64}, meshsize::SVector{2, Int64}, delta_sizes::SVector{2, Float64}, paramtrs::Tuple{Float64, String})
     μ = paramtrs[1]
     order = paramtrs[2]
     N = meshsize[1]
    (Δθ, Δη) = delta_sizes

    if  n > 2 && n < N

        return( ψ[n-2,m]  -4*ψ[n-1,m] +6*ψ[n,m] -4*ψ[n+1,m] + ψ[n+2,m])

    elseif n == 2 #Second order only

        return( (-ψ[n,m]) -4*ψ[n-1,m] +6*ψ[n,m] -4*ψ[n+1,m] + ψ[n+2,m])

    elseif n == N #Second order only

        return( ψ[n-2,m]  -4*ψ[n-1,m] +6*ψ[n,m] -4*ψ[n+1,m] + (ψ[n,m] - 2.0*Δη*μ*sin(Δθ*(m-1))))
    else
        println("Error in ψ_ηηηη n =$n")
        return 0.0
    end
end

function ψ_ηηη(n ::Int64, m:: Int64, ψ::Matrix{Float64}, meshsize::SVector{2, Int64}, delta_sizes::SVector{2, Float64}, paramtrs::Tuple{Float64, String})
     μ = paramtrs[1]
     order = paramtrs[2]
     N = meshsize[1]
    (Δθ, Δη) = delta_sizes

    if  n > 2 && n < N
        if n > 3 && n < N - 1 && order == "3rdOrder"
          return( ψ[n-3,m]/8 - ψ[n-2,m] + 13*ψ[n-1,m]/8 - 13*ψ[n+1,m]/8 + ψ[n+2,m] - ψ[n+3,m]/8)
        else
          return(-0.5*ψ[n-2,m] + ψ[n-1,m] - ψ[n+1,m] + 0.5*ψ[n+2,m])
        end

    elseif n == 2

        return(-0.5*(-ψ[n,m]) + ψ[n-1,m] - ψ[n+1,m] + 0.5*ψ[n+2,m])

    elseif n == N
        return(-0.5*ψ[n-2,m] + ψ[n-1,m] - ψ[n+1,m] + 0.5*(ψ[n,m] - 2.0*Δη*μ*sin(Δθ*(m-1))))
        #return(0.25*ψ[n-3,m]-1.75*ψ[n-2,m] + 3.5*ψ[n-1,m] -2.5*ψ[n,m] + 0.25*ψ[n+1,m] + 0.25*(ψ[n,m] - 2.0*Δη*μ*sin(Δθ*(m-1))))
    else
        println("Error in ψ_ηηη n =$n")
        return 0.0
    end
end

function ψ_ηη(n ::Int64, m:: Int64, ψ::Matrix{Float64}, meshsize::SVector{2, Int64})
    N = meshsize[1]
    if  n == 2 || n == N

        return(  ψ[n-1,m] -2*ψ[n,m]+ ψ[n+1,m] )
    elseif n > 2  && n < N
        return ( -ψ[n-2,m]/12 +4*ψ[n-1,m]/3 -2.5*ψ[n,m]+ 4*ψ[n+1,m]/3 -ψ[n+2,m]/12)
    else
        println("Error in ψ_ηη n =$n")
        return 0.0
    end
end

function ψ_η(n ::Int64, m:: Int64, ψ::Matrix{Float64},  meshsize::SVector{2, Int64})
    N = meshsize[1]
    if  n == 2 || n == N
        return(  -0.5*ψ[n-1,m] + 0.5*ψ[n+1,m] )

    elseif  n>=3 && n<= N-1
        return(  ψ[n-2,m]/12 -2*ψ[n-1,m]/3 + 2*ψ[n+1,m]/3 - ψ[n+2,m]/12 )

    else
        println("Error in ψ_η n =$n")
        return 0.0
    end
end

"""The symmetric (odd in η) asymptotic solution for the streamfunction for small \$R_s\$ with 5 terms"""
function Ψ_smallr(θ::Float64, η::Float64 ,r::Float64, number_of_terms::Int)
    terms = [
        -η*(η^2-1)*sin(θ)/2.0,

        r*η*(η^2-1)^2.0*(2.0+η^2)*sin(2*θ)/560.0,

        r^2.0*η*(η^2-1)^2*((-591 -2294*η^2 + 161*η^4 + 1428*η^6)*sin(θ) +
            +3.0*(423 + 166*η^2 - 553*η^4 - 84*η^6)sin(3*θ))/62092800.0,

        r^3.0*η*(η - 1)^2*(η + 1)^2*(2184*η^10*cos(θ)^2 + 1540*η^10 + 24213*η^8*cos(θ)^2 - 44205*η^8 +
            + 18942*η^6*cos(θ)^2 + 33446*η^6 - 101444*η^4*cos(θ)^2 + 66052*η^4 - 23086*η^2*cos(θ)^2 -
            6746*η^2 - 14889*cos(θ)^2 - 9383)*sin(θ)*cos(θ)/28252224000,

        r^4.0*η*(η - 1)^2*(η + 1)^2*(968647680*η^14*sin(θ)^5 + 14774464320*η^14*sin(θ) + 3510571680*η^14*sin(3*θ) -
            393513120*η^14*sin(5*θ) - 39671478000*η^12*sin(θ)^5 - 75537556710*η^12*sin(θ) -
            28276357725*η^12*sin(3*θ) - 3752563815*η^12*sin(5*θ) - 181021246560*η^10*sin(θ)^5 +
            357286638324*η^10*sin(θ) + 75142631718*η^10*sin(3*θ) - 1180776366*η^10*sin(5*θ) +
            677920708080*η^8*sin(θ)^5 - 645834258762*η^8*sin(θ) - 123813530379*η^8*sin(3*θ) -
            1711397457*η^8*sin(5*θ) + 193167076032*η^6*sin(θ)^5 - 82870720296*η^6*sin(θ) +
            215941919508*η^6*sin(3*θ) + 59820477948*η^6*sin(5*θ) + 153978403824*η^4*sin(θ)^5 -
            123221433370*η^4*sin(θ) + 181687022685*η^4*sin(3*θ) - 60662329497*η^4*sin(5*θ) -
            500785425504*η^2*sin(θ)^5 + 367229681332*η^2*sin(θ) - 134582502330*η^2*sin(3*θ) -
            33288605262*η^2*sin(5*θ) - 1507832445168*sin(θ)^5 + 969877856394*sin(θ) - 482955016725*sin(3*θ) -
            9092499471*sin(5*θ))/16189310893916160000
        ]
    if number_of_terms < 6
        return sum(terms[1:number_of_terms])
    else
        return term[1]
    end
end

"""First η-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function Ψ_η_smallr(θ::Float64, η::Float64 ,r::Float64)
    return(-(3.0*η^2-1)*sin(θ)/2.0 +
        +r*(7*η^6-9*η^2+2)*sin(2*θ)/560.0 +
        + r^2.0*(3*(-197 - 1112*η^2 + 6930*η^4 - 2772*η^6 - 8085*η^8 + 5236*η^10)*sin(θ) + (1269 - 6120*η^2 - 6930*η^4 + 24948*η^6 - 10395*η^8 - 2772*η^10)*sin(3*θ))/62092800.0)
end

"""Second η-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function  Ψ_ηη_smallr(θ::Float64, η::Float64 ,r::Float64)
    return(-3.0*η*sin(θ) + r*(42*η^5-18*η)*sin(2*θ)/560.0 +
        r^2.0*(3*( - 2224*η + 27720*η^3 - 16632*η^5 - 64680*η^7 + 52360*η^9)*sin(θ) +
            (- 12240*η - 27720*η^3 + 149688*η^5 - 83160*η^7 - 27720*η^9)*sin(3*θ))/62092800.0)
end

"""Third η-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function  Ψ_ηηη_smallr(θ::Float64, η::Float64 ,r::Float64)
    return(-3.0*sin(θ) + r*(210*η^4-18)*sin(2*θ)/560.0 +
        r^2.0*(3*( - 2224 + 83160*η^2 - 83160*η^4 - 452760*η^6 + 471240*η^8)*sin(θ) + (- 12240 - 83160*η^2 + 748440*η^4 - 582120*η^6 - 249480*η^8)*sin(3*θ))/62092800.0)
end

"""Fourth η-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function  Ψ_ηηηη_smallr(θ::Float64, η::Float64 ,r::Float64)
    return(3*r*η*(3*r*η^6*sin(θ)^2/70 + r*η^6/35 + 3*r*η^4*sin(θ)^2/40 - r*η^4/10 - 9*r*η^2*sin(θ)^2/140 + 3*r*η^2/70 + r*sin(θ)^2/280 + η^2*cos(θ))*sin(θ))
end

"""1st θ-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function  Ψ_θ_smallr(θ::Float64, η::Float64 ,r::Float64)
return (-η*(η - 1)*(η + 1)*(756*r^2*η^8*cos(θ)^3 - 924*r^2*η^8*cos(θ) + 4221*r^2*η^6*cos(θ)^3 - 2849*r^2*η^6*cos(θ) - 6471*r^2*η^4*cos(θ)^3 + 5467*r^2*η^4*cos(θ) - 2313*r^2*η^2*cos(θ)^3 + 1309*r^2*η^2*cos(θ) + 3807*r^2*cos(θ)^3 - 3003*r^2*cos(θ) - 55440*r*η^4*cos(2*θ) - 55440*r*η^2*cos(2*θ) + 110880*r*cos(2*θ) + 7761600*cos(θ))/15523200)
end

"""ηηθ-derivative of the symmetric streamfunction calculated from the small \$R_s\$ asymptotic series (3 terms)"""
function  Ψ_ηηθ_smallr(θ::Float64, η::Float64 ,r::Float64)
return (η*(3465*r^2*η^8*sin(θ)^2*cos(θ) + 770*r^2*η^8*cos(θ) + 10395*r^2*η^6*sin(θ)^2*cos(θ) - 4620*r^2*η^6*cos(θ) - 18711*r^2*η^4*sin(θ)^2*cos(θ) + 4158*r^2*η^4*cos(θ) + 3465*r^2*η^2*sin(θ)^2*cos(θ) + 1530*r^2*sin(θ)^2*cos(θ) - 452*r^2*cos(θ) + 97020*r*η^4*cos(2*θ) - 41580*r*cos(2*θ) - 1940400*cos(θ))/646800)
end

function rhs_nonlinear_terms( n::Int64, m::Int64,k::Int64, ψ::Matrix{Float64}, meshsize::SVector{2, Int64}, delta_sizes::SVector{2, Float64}, paramtrs::Tuple{Float64, String})
    if m > 0 && k > 0
        return( -ψ_ηηη(n, m, ψ, meshsize, delta_sizes, paramtrs)*ψ[n, k] + ψ_η(n, m, ψ, meshsize)*ψ_ηη(n, k, ψ, meshsize))
    else
        return 0
    end
end

"""The symmetric (odd in η) asymptotic solution for the streamfunction for large \$R_s\$ with 1 term"""
function Ψ_bigr(θ::Float64, η::Float64)
    return(sin(pi*η)*sin(θ)/pi)
end

function choose_scheme_coefficients!(m::Int64, Order::String, s::Vector{Float64})
    if m == 2
       @.  a = [0, 0 ,-1, 1]
    elseif m == 3 && Order != "1stOrder"
       @. a = [0 ,-0.5, 0 , 0.5]
    elseif m == 4 && Order =="3rdOrder"
       @. a = [1/6.0,-1,0.5,1/3.0]
    end
end

```

    choose_scheme_coefficients! (generic function with 1 method)

```julia
const N = 512;  # Total number of intervals (steps-1) in the η direction
const M = 128;  # Total number of intervals (steps-1) in the θ direction
ψ_j = zeros(N+1,M+1); # Initialise the matrix for the streamfunction at the current time step
const Δη = 1.0/N;
const Δθ = pi/M;
const λ = 0.0 ;           # Implicit-explicit cancelling coeffient of ψ_ηηηη term, Jens Eggers damping method
const K = 2;    #The number of iterations to carry out at each theta step
const μ = 1.0
#const c    =  Δη/Δθ
matrix_condition_numb = zeros(M+1);
matrix_determinant = zeros(M+1);
matrix_positive_eigenvals = zeros(M+1);

meshsize = SA{Int64}[N,M]
delta_sizes= SA{Float64}[Δθ, Δη]
paramtrs =  (μ, "1stOrder")

rey_number = 10.0
```

    10.0

```julia
d3  =  1.0*Δθ/(rey_number*Δη) + 1.0*λ        #Diagonal entries for the matrix problem
d2  = -4.0*Δθ/(rey_number*Δη) - 4.0*λ
d1  =  6.0*Δθ/(rey_number*Δη) + 6.0*λ

θ_order = "2ndOrder"
a = [0.0,0.0,-1.0,1.0]
rhs = zeros(N-1);

matrix_save = 0
for m = 2:M+1
    ψ_next = @view ψ_j[2:N,m]
    #ψ_next .=  ψ_j[2:N,m-1]

    for k = 0:K #This iterates the solution at each θ-step in order to get an implicit like approximation by recalculating the explicit terms implicitly

    k > 0 ? p = 1 : p = 0;

    @. rhs = 0.0
    choose_scheme_coefficients!(m, θ_order, a)
    A =spdiagm(-2=>zeros(N-3), -1 => zeros(N-2), 0 => zeros(N-1), 1 => zeros(N-2), 2 => zeros(N-3))
    row = 1
    n = row + 1
    #A[row,row-2]  = nuemann boundary condition applied and add 0 to rhs
    #A[row,row-1] = 0 dirichlet boundary condition applied, this term is zero
    A[row,row]    = -d3 + d1 + ψ_ηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*a[4] +2*ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row+1]  = d2 -ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row+2]  = d3

    rhs[row]      = rhs_nonlinear_terms( n, m-1+p, m-1, ψ_j, meshsize, delta_sizes, paramtrs)*a[3]+
                    +rhs_nonlinear_terms( n, m-1+p, m-2, ψ_j, meshsize, delta_sizes, paramtrs)*a[2]+
                    +rhs_nonlinear_terms( n, m-1+p, m-3, ψ_j, meshsize, delta_sizes, paramtrs)*a[1]+
                    +ψ_ηηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*λ

    row = 2
    n = row + 1
    #A[row,row-2]  = d3 dirichlet boundary condition applied, this term is zero
    A[row,row-1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row]    = d1 + ψ_ηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*a[4] +2*ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row+1]  = d2 -ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row+2]  = d3

    rhs[row]      = rhs_nonlinear_terms( n, m-1+p, m-1, ψ_j, meshsize, delta_sizes, paramtrs)*a[3]+
                   +rhs_nonlinear_terms( n, m-1+p, m-2, ψ_j, meshsize, delta_sizes, paramtrs)*a[2]+
                   +rhs_nonlinear_terms( n, m-1+p, m-3, ψ_j, meshsize, delta_sizes, paramtrs)*a[1]+
                   +ψ_ηηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*λ

    for row = 3:N-3
        n = row + 1
        A[row,row-2]  = d3
        A[row,row-1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
        A[row,row]    = d1 + ψ_ηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*a[4] +2*ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
        A[row,row+1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
        A[row,row+2]  = d3

        rhs[row]      = +rhs_nonlinear_terms( n, m-1+p, m-1, ψ_j, meshsize, delta_sizes, paramtrs)*a[3]+
                        +rhs_nonlinear_terms( n, m-1+p, m-2, ψ_j, meshsize, delta_sizes, paramtrs)*a[2]+
                        +rhs_nonlinear_terms( n, m-1+p, m-3, ψ_j, meshsize, delta_sizes, paramtrs)*a[1]+
                        +ψ_ηηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*λ
    end

    row = N-2
    n = row + 1
    A[row,row-2]  = d3
    A[row,row-1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row]    = d1 + ψ_ηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*a[4] +2*ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row+1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    #A[row,row+2]  = d3 dirichlet boundary condition applied, this term is zero

    rhs[row]      =  rhs_nonlinear_terms( n, m-1+p, m-1, ψ_j, meshsize, delta_sizes, paramtrs)*a[3]+
                    +rhs_nonlinear_terms( n, m-1+p, m-2, ψ_j, meshsize, delta_sizes, paramtrs)*a[2]+
                    +rhs_nonlinear_terms( n, m-1+p, m-3, ψ_j, meshsize, delta_sizes, paramtrs)*a[1]+
                    +ψ_ηηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*λ

    row = N-1
    n = row + 1
    A[row,row-2]  = d3
    A[row,row-1]  = d2 - ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    A[row,row]    = d3 + d1 + ψ_ηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*a[4] +2*ψ_η(n, m-1+p, ψ_j, meshsize)*a[4]
    #A[row,row+1]  = d2 -ψ_η(n, m-1, ψ_j, meshsize)*c  dirichlet boundary condition applied, this term is zero
    #A[row,row+2]  = d3 nuemann boundary condition applied and add +2Δημsin(θ)*d3 to rhs

    rhs[row]      =  rhs_nonlinear_terms( n, m-1+p, m-1, ψ_j, meshsize, delta_sizes, paramtrs)*a[3]+
                    +rhs_nonlinear_terms( n, m-1+p, m-2, ψ_j, meshsize, delta_sizes, paramtrs)*a[2]+
                    +rhs_nonlinear_terms( n, m-1+p, m-3, ψ_j, meshsize, delta_sizes, paramtrs)*a[1]+
                    +ψ_ηηηη(n, m-1+p, ψ_j, meshsize, delta_sizes, paramtrs)*λ +
                    + 2*Δη*μ*sin(Δθ*(m-1))*d3

    #LUA = lu(A, check=true)
    #LUA2= lu(A)

    ψ_next .= A\rhs
    #ldiv!(LUA2,rhs)
    #P = ilu(A , τ = 0.1)
    #results = gmres!(ψ_next, A, rhs, Pl = P,log=true, maxiter = 100)
    #results = gmres!(ψ_next, A, rhs, log=true, maxiter = 10)
    #matrix_condition_numb[m] = cond(Matrix(A),2)
    #matrix_determinant[m] = det(A)
    #matrix_positive_eigenvals[m] = findmin(real.(eigs(A, nev= N-3)[1]))[1]
    #println("m = $m, cond(A) = $(round(matrix_condition_numb[m],sigdigits=3)), det(A) = $(round(matrix_determinant[m],sigdigits=3)), (results[end]), Norm error $(round(norm(A*ψ_next - rhs, Inf),sigdigits=3))")
    println("m = $m, Norm error $(round(norm(A*ψ_next - rhs, Inf),sigdigits=3))") #$(results[end])
    #matrix_save = A
    A = nothing  #This frees up the memory on the next automated garbage collection GC.gc()
    end #End the K-Loop

    if abs(ψ_next[round(Int,N/2)]/μ) > 10.0
            println("\nψ is becoming unbounded ψ = $(ψ_next[round(Int,N/2)]) \nEnding code")
            ψ_next .=  0.0
            break
    end
end

```

    m = 2, Norm error 1.13e-17
    m = 2, Norm error 1.82e-17
    m = 2, Norm error 1.47e-17
    m = 3, Norm error 3.12e-17
    m = 3, Norm error 3.12e-17
    m = 3, Norm error 3.3e-17
    m = 4, Norm error 4.66e-17
    m = 4, Norm error 5.36e-17
    m = 4, Norm error 4.04e-17
    m = 5, Norm error 6.07e-17
    m = 5, Norm error 6.17e-17
    m = 5, Norm error 5.93e-17
    m = 6, Norm error 8.47e-17
    m = 6, Norm error 7.06e-17
    m = 6, Norm error 7.68e-17
    m = 7, Norm error 1.02e-16
    m = 7, Norm error 6.67e-17
    m = 7, Norm error 8.97e-17
    m = 8, Norm error 1.12e-16
    m = 8, Norm error 1.17e-16
    m = 8, Norm error 1.08e-16
    m = 9, Norm error 1.2e-16
    m = 9, Norm error 1.21e-16
    m = 9, Norm error 1.29e-16
    m = 10, Norm error 1.53e-16
    m = 10, Norm error 1.44e-16
    m = 10, Norm error 1.3e-16
    m = 11, Norm error 1.56e-16
    m = 11, Norm error 1.64e-16
    m = 11, Norm error 1.44e-16
    m = 12, Norm error 1.63e-16
    m = 12, Norm error 1.45e-16
    m = 12, Norm error 1.99e-16
    m = 13, Norm error 1.73e-16
    m = 13, Norm error 2.44e-16
    m = 13, Norm error 1.64e-16
    m = 14, Norm error 2.28e-16
    m = 14, Norm error 2.21e-16
    m = 14, Norm error 1.7e-16
    m = 15, Norm error 2.48e-16
    m = 15, Norm error 2.03e-16
    m = 15, Norm error 2.03e-16
    m = 16, Norm error 2.28e-16
    m = 16, Norm error 2.49e-16
    m = 16, Norm error 2.77e-16
    m = 17, Norm error 2.81e-16
    m = 17, Norm error 2.33e-16
    m = 17, Norm error 2.66e-16
    m = 18, Norm error 2.34e-16
    m = 18, Norm error 2.22e-16
    m = 18, Norm error 2.46e-16
    m = 19, Norm error 2.69e-16
    m = 19, Norm error 2.62e-16
    m = 19, Norm error 2.4e-16
    m = 20, Norm error 3.07e-16
    m = 20, Norm error 3.06e-16
    m = 20, Norm error 3.45e-16
    m = 21, Norm error 3.46e-16
    m = 21, Norm error 2.39e-16
    m = 21, Norm error 2.43e-16
    m = 22, Norm error 3.52e-16
    m = 22, Norm error 2.95e-16
    m = 22, Norm error 2.87e-16
    m = 23, Norm error 2.99e-16
    m = 23, Norm error 2.63e-16
    m = 23, Norm error 3.98e-16
    m = 24, Norm error 3.13e-16
    m = 24, Norm error 3.89e-16
    m = 24, Norm error 2.82e-16
    m = 25, Norm error 3.86e-16
    m = 25, Norm error 3.6e-16
    m = 25, Norm error 3.27e-16
    m = 26, Norm error 3.55e-16
    m = 26, Norm error 3.83e-16
    m = 26, Norm error 3.3e-16
    m = 27, Norm error 3.1e-16
    m = 27, Norm error 3.09e-16
    m = 27, Norm error 4.79e-16
    m = 28, Norm error 4.31e-16
    m = 28, Norm error 3.39e-16
    m = 28, Norm error 4.22e-16
    m = 29, Norm error 3.78e-16
    m = 29, Norm error 4.78e-16
    m = 29, Norm error 3.36e-16
    m = 30, Norm error 4.57e-16
    m = 30, Norm error 3.61e-16
    m = 30, Norm error 5.55e-16
    m = 31, Norm error 4.25e-16
    m = 31, Norm error 4.18e-16
    m = 31, Norm error 4.32e-16
    m = 32, Norm error 3.92e-16
    m = 32, Norm error 4.1e-16
    m = 32, Norm error 4.17e-16
    m = 33, Norm error 5.78e-16
    m = 33, Norm error 5.19e-16
    m = 33, Norm error 5.08e-16
    m = 34, Norm error 4.63e-16
    m = 34, Norm error 4.24e-16
    m = 34, Norm error 4.24e-16
    m = 35, Norm error 5.43e-16
    m = 35, Norm error 5.11e-16
    m = 35, Norm error 5.41e-16
    m = 36, Norm error 4.92e-16
    m = 36, Norm error 4.58e-16
    m = 36, Norm error 4.37e-16
    m = 37, Norm error 4.23e-16
    m = 37, Norm error 4.79e-16
    m = 37, Norm error 6.13e-16
    m = 38, Norm error 4.34e-16
    m = 38, Norm error 4.72e-16
    m = 38, Norm error 4.74e-16
    m = 39, Norm error 4.91e-16
    m = 39, Norm error 5.18e-16
    m = 39, Norm error 5.12e-16
    m = 40, Norm error 4.85e-16
    m = 40, Norm error 5.27e-16
    m = 40, Norm error 4.31e-16
    m = 41, Norm error 5.85e-16
    m = 41, Norm error 4.51e-16
    m = 41, Norm error 5.27e-16
    m = 42, Norm error 4.95e-16
    m = 42, Norm error 5.82e-16
    m = 42, Norm error 4.98e-16
    m = 43, Norm error 4.44e-16
    m = 43, Norm error 5.23e-16
    m = 43, Norm error 6.53e-16
    m = 44, Norm error 4.55e-16
    m = 44, Norm error 4.86e-16
    m = 44, Norm error 5.16e-16
    m = 45, Norm error 4.83e-16
    m = 45, Norm error 4.84e-16
    m = 45, Norm error 5.48e-16
    m = 46, Norm error 5.36e-16
    m = 46, Norm error 6.1e-16
    m = 46, Norm error 5.19e-16
    m = 47, Norm error 5.39e-16
    m = 47, Norm error 5.3e-16
    m = 47, Norm error 5.95e-16
    m = 48, Norm error 5.3e-16
    m = 48, Norm error 5.28e-16
    m = 48, Norm error 5.23e-16
    m = 49, Norm error 5.68e-16
    m = 49, Norm error 6.02e-16
    m = 49, Norm error 5.45e-16
    m = 50, Norm error 4.59e-16
    m = 50, Norm error 6.2e-16
    m = 50, Norm error 5.97e-16
    m = 51, Norm error 4.79e-16
    m = 51, Norm error 5.37e-16
    m = 51, Norm error 5.75e-16
    m = 52, Norm error 4.86e-16
    m = 52, Norm error 5.4e-16
    m = 52, Norm error 4.51e-16
    m = 53, Norm error 5.28e-16
    m = 53, Norm error 4.99e-16
    m = 53, Norm error 5.24e-16
    m = 54, Norm error 6.12e-16
    m = 54, Norm error 7.24e-16
    m = 54, Norm error 4.74e-16
    m = 55, Norm error 5.99e-16
    m = 55, Norm error 5.59e-16
    m = 55, Norm error 5.6e-16
    m = 56, Norm error 5.96e-16
    m = 56, Norm error 6.14e-16
    m = 56, Norm error 5.62e-16
    m = 57, Norm error 6.46e-16
    m = 57, Norm error 5.54e-16
    m = 57, Norm error 5.98e-16
    m = 58, Norm error 4.63e-16
    m = 58, Norm error 7.38e-16
    m = 58, Norm error 6.01e-16
    m = 59, Norm error 5.14e-16
    m = 59, Norm error 6.09e-16
    m = 59, Norm error 5.08e-16
    m = 60, Norm error 5.68e-16
    m = 60, Norm error 5.15e-16
    m = 60, Norm error 5.93e-16
    m = 61, Norm error 4.58e-16
    m = 61, Norm error 5.56e-16
    m = 61, Norm error 5.15e-16
    m = 62, Norm error 4.51e-16
    m = 62, Norm error 4.28e-16
    m = 62, Norm error 4.89e-16
    m = 63, Norm error 6.06e-16
    m = 63, Norm error 6.0e-16
    m = 63, Norm error 6.52e-16
    m = 64, Norm error 5.17e-16
    m = 64, Norm error 5.71e-16
    m = 64, Norm error 5.03e-16
    m = 65, Norm error 6.32e-16
    m = 65, Norm error 5.98e-16
    m = 65, Norm error 4.99e-16
    m = 66, Norm error 5.59e-16
    m = 66, Norm error 5.38e-16
    m = 66, Norm error 5.16e-16
    m = 67, Norm error 5.99e-16
    m = 67, Norm error 5.75e-16
    m = 67, Norm error 5.97e-16
    m = 68, Norm error 7.55e-16
    m = 68, Norm error 6.27e-16
    m = 68, Norm error 6.13e-16
    m = 69, Norm error 5.71e-16
    m = 69, Norm error 5.01e-16
    m = 69, Norm error 5.97e-16
    m = 70, Norm error 4.99e-16
    m = 70, Norm error 4.78e-16
    m = 70, Norm error 5.08e-16
    m = 71, Norm error 5.39e-16
    m = 71, Norm error 4.85e-16
    m = 71, Norm error 6.36e-16
    m = 72, Norm error 6.28e-16
    m = 72, Norm error 5.51e-16
    m = 72, Norm error 5.62e-16
    m = 73, Norm error 6.01e-16
    m = 73, Norm error 6.15e-16
    m = 73, Norm error 5.83e-16
    m = 74, Norm error 5.1e-16
    m = 74, Norm error 5.8e-16
    m = 74, Norm error 4.98e-16
    m = 75, Norm error 5.02e-16
    m = 75, Norm error 4.97e-16
    m = 75, Norm error 5.78e-16
    m = 76, Norm error 5.52e-16
    m = 76, Norm error 4.71e-16
    m = 76, Norm error 5.95e-16
    m = 77, Norm error 4.94e-16
    m = 77, Norm error 6.11e-16
    m = 77, Norm error 6.76e-16
    m = 78, Norm error 5.09e-16
    m = 78, Norm error 5.13e-16
    m = 78, Norm error 6.36e-16
    m = 79, Norm error 5.46e-16
    m = 79, Norm error 5.49e-16
    m = 79, Norm error 5.56e-16
    m = 80, Norm error 5.45e-16
    m = 80, Norm error 5.72e-16
    m = 80, Norm error 6.26e-16
    m = 81, Norm error 5.34e-16
    m = 81, Norm error 4.49e-16
    m = 81, Norm error 6.39e-16
    m = 82, Norm error 6.55e-16
    m = 82, Norm error 4.45e-16
    m = 82, Norm error 5.12e-16
    m = 83, Norm error 5.06e-16
    m = 83, Norm error 5.02e-16
    m = 83, Norm error 5.79e-16
    m = 84, Norm error 4.95e-16
    m = 84, Norm error 5.27e-16
    m = 84, Norm error 5.75e-16
    m = 85, Norm error 5.46e-16
    m = 85, Norm error 4.51e-16
    m = 85, Norm error 4.69e-16
    m = 86, Norm error 4.95e-16
    m = 86, Norm error 6.38e-16
    m = 86, Norm error 4.74e-16
    m = 87, Norm error 4.34e-16
    m = 87, Norm error 5.08e-16
    m = 87, Norm error 4.46e-16
    m = 88, Norm error 5.45e-16
    m = 88, Norm error 5.53e-16
    m = 88, Norm error 4.51e-16
    m = 89, Norm error 4.53e-16
    m = 89, Norm error 4.85e-16
    m = 89, Norm error 4.6e-16
    m = 90, Norm error 4.69e-16
    m = 90, Norm error 4.27e-16
    m = 90, Norm error 4.88e-16
    m = 91, Norm error 3.99e-16
    m = 91, Norm error 4.38e-16
    m = 91, Norm error 5.49e-16
    m = 92, Norm error 6.09e-16
    m = 92, Norm error 4.83e-16
    m = 92, Norm error 3.49e-16
    m = 93, Norm error 4.68e-16
    m = 93, Norm error 5.21e-16
    m = 93, Norm error 3.84e-16
    m = 94, Norm error 4.44e-16
    m = 94, Norm error 4.15e-16
    m = 94, Norm error 4.49e-16
    m = 95, Norm error 4.18e-16
    m = 95, Norm error 3.78e-16
    m = 95, Norm error 4.2e-16
    m = 96, Norm error 3.42e-16
    m = 96, Norm error 4.05e-16
    m = 96, Norm error 3.76e-16
    m = 97, Norm error 3.39e-16
    m = 97, Norm error 3.85e-16
    m = 97, Norm error 3.25e-16
    m = 98, Norm error 3.44e-16
    m = 98, Norm error 4.09e-16
    m = 98, Norm error 3.28e-16
    m = 99, Norm error 3.02e-16
    m = 99, Norm error 3.34e-16
    m = 99, Norm error 3.8e-16
    m = 100, Norm error 3.21e-16
    m = 100, Norm error 3.7e-16
    m = 100, Norm error 3.04e-16
    m = 101, Norm error 3.76e-16
    m = 101, Norm error 3.09e-16
    m = 101, Norm error 3.44e-16
    m = 102, Norm error 2.89e-16
    m = 102, Norm error 2.91e-16
    m = 102, Norm error 3.38e-16
    m = 103, Norm error 2.76e-16
    m = 103, Norm error 4.45e-16
    m = 103, Norm error 2.64e-16
    m = 104, Norm error 2.73e-16
    m = 104, Norm error 3.03e-16
    m = 104, Norm error 3.14e-16
    m = 105, Norm error 2.5e-16
    m = 105, Norm error 2.86e-16
    m = 105, Norm error 2.63e-16
    m = 106, Norm error 2.35e-16
    m = 106, Norm error 2.55e-16
    m = 106, Norm error 2.65e-16
    m = 107, Norm error 3.68e-16
    m = 107, Norm error 2.19e-16
    m = 107, Norm error 3.1e-16
    m = 108, Norm error 2.63e-16
    m = 108, Norm error 2.24e-16
    m = 108, Norm error 2.79e-16
    m = 109, Norm error 2.36e-16
    m = 109, Norm error 2.77e-16
    m = 109, Norm error 2.57e-16
    m = 110, Norm error 3.31e-16
    m = 110, Norm error 2.0e-16
    m = 110, Norm error 2.58e-16
    m = 111, Norm error 1.82e-16
    m = 111, Norm error 3.07e-16
    m = 111, Norm error 2.38e-16
    m = 112, Norm error 1.74e-16
    m = 112, Norm error 2.07e-16
    m = 112, Norm error 2.08e-16
    m = 113, Norm error 2.65e-16
    m = 113, Norm error 2.13e-16
    m = 113, Norm error 1.78e-16
    m = 114, Norm error 1.82e-16
    m = 114, Norm error 1.69e-16
    m = 114, Norm error 1.97e-16
    m = 115, Norm error 1.61e-16
    m = 115, Norm error 1.57e-16
    m = 115, Norm error 1.64e-16
    m = 116, Norm error 1.41e-16
    m = 116, Norm error 2.01e-16
    m = 116, Norm error 1.75e-16
    m = 117, Norm error 1.22e-16
    m = 117, Norm error 1.44e-16
    m = 117, Norm error 1.82e-16
    m = 118, Norm error 1.38e-16
    m = 118, Norm error 1.25e-16
    m = 118, Norm error 1.32e-16
    m = 119, Norm error 1.13e-16
    m = 119, Norm error 1.44e-16
    m = 119, Norm error 1.32e-16
    m = 120, Norm error 1.07e-16
    m = 120, Norm error 9.94e-17
    m = 120, Norm error 1.23e-16
    m = 121, Norm error 1.02e-16
    m = 121, Norm error 1.07e-16
    m = 121, Norm error 9.86e-17
    m = 122, Norm error 9.89e-17
    m = 122, Norm error 8.81e-17
    m = 122, Norm error 1.11e-16
    m = 123, Norm error 7.88e-17
    m = 123, Norm error 8.74e-17
    m = 123, Norm error 7.43e-17
    m = 124, Norm error 6.68e-17
    m = 124, Norm error 5.61e-17
    m = 124, Norm error 6.65e-17
    m = 125, Norm error 4.55e-17
    m = 125, Norm error 5.5e-17
    m = 125, Norm error 4.88e-17
    m = 126, Norm error 3.93e-17
    m = 126, Norm error 3.56e-17
    m = 126, Norm error 3.56e-17
    m = 127, Norm error 2.66e-17
    m = 127, Norm error 2.73e-17
    m = 127, Norm error 2.58e-17
    m = 128, Norm error 1.06e-17
    m = 128, Norm error 1.39e-17
    m = 128, Norm error 1.09e-17
    m = 129, Norm error 1.71e-18
    m = 129, Norm error 3.41e-20
    m = 129, Norm error 1.26e-20

```julia
pyplot()
psi_sol = reshape(ψ_j,length(ψ_j));
η_mesh = [(-1 +(n-1)*Δη)  for m=1:M+1 for n=1:N+1];
θ_mesh = [((m-1)*Δθ) for m=1:M+1 for n=1:N+1 ];
η_vector = range(-1,stop = 1,length = N+1);
θ_vector = range(0,stop = pi,length = M+1);

```

```julia
compare_with_timemarch = true
ψ_tm = 0;
sf = 1; #Scalefactor for the stored solution

if compare_with_timemarch# Load file with matrix N by M
    try
    ψ_tm =readdlm("psi_solution_R_45.0_1stOrder_sym.txt",Float64,); #Check that N and M dimensions match
    if size(ψ_tm) == (N+1,M+1)
        print("Meshsize dimensions of the timemarched ψ match with the θ-marched solution \nthis is good!")
    else
        print("Dimensions of timemarched  $(size(ψ_tm)) ψ DO NOT match with the θ-marched ($(N+1),$(M+1))\n ERROR..")
            if   M%(size(ψ_tm)[2] -1) == 0
                println("A comparison with time-marched can still be made with a scalefactor")
                sf = round(Int,M/(size(ψ_tm)[2] -1));
            else
                compare_with_timemarch = false;
            end
    end
    catch e
        println("Could not open file don't worry code will continue regardless")
        compare_with_timemarch = false;
    end
end
sf
```

    Dimensions of timemarched  (129, 361) ψ DO NOT match with the θ-marched (513,129)
     ERROR..

    1

```julia
pyplot()
wireframe( range(-1.0,stop = 1.0,length = round(Int,N/8)+1),range(0.0,stop = pi,length = round(Int,M/8)+1), transpose(ψ_j[1:8:end,1:8:end]), colour=:greys, fmt = :svg)
yticks!(([0:π/6:π;], ["0", "π/6", "π/3", "π/2", "2π/3", "5π/6","π"]))
plot!(xlabel=L"\eta", ylabel=L"\theta", zlabel=L"\Psi", title=L"R_s = %$rey_number"*"\n"*L"N_η=%$N, M_θ=%$M", size=(1000,800),camera=(-60,20), zlims=(-0.1,0.2) )
plot!(xtickfont = font(12), ytickfont = font(12), ztickfont = font(12), titlefont = font(11))

#plot!(xlabel=L"η", ylabel=L"θ", zlabel=L"Ψ", title=L"R_s = %$rey_number"*"\n"*L"N_η=%$N, M_θ=%$M")
plot!(xtickfont = font(12), ytickfont = font(12), ztickfont = font(12), titlefont=font(10))
```

```julia

m_at_fail = M+1
for m = 1:M+1
    if abs(findmax(abs.(ψ_j[:,m]))[1])/μ > 0.4
        print("Convergence fails at m = $m \ni.e near θ = $(m-1)π/$M\u1D9C radians $(round((m-1)*Δθ,digits=1))\u1D9C \nθ = $(round((m-1)*180/M,digits=1))\U000B0 degrees R_s = $rey_number")
        m_at_fail = m
        break
    elseif m == M+1
        println("Solution seems to be smooth")
    end

end
m=m_at_fail - 2
m = 20
radian_string = "$(round(Δθ*(m-1),digits=1))\u1D9C"
eta=range(0, stop=1,length= N+1)
theta_march_plt = plot(eta, ψ_j[:,m]/μ, label=θ_order*" θ-marched\n steady solution", linecolor=:red,  linestyle=:dot,   linewidth =2, legend=:outertopright, size=(1100,500), show = true)

xlabel!("η \n"*L"N_η=%$N, M_θ=%$M");
ylabel!(L"Ψ", yguidefontrotation=-90);
title!("η-profile  "*L"θ=\frac{%$(m-1)}{%$M}π"*" or "*radian_string*L"\quad R_s = %$rey_number");
if compare_with_timemarch
    plot!(theta_march_plt, range(0, stop=1,length=size(ψ_tm)[1]), ψ_tm[:,round(Int64,(m-1)/sf)+1], label="timemarched numerical \nsolution", linecolor=:cyan,    linestyle=:dot,   linewidth =2, legend=:outertopright, size=(1100,500));
end
if rey_number < 60.0
 plot!(theta_march_plt,x->Ψ_smallr(Δθ*(m-1),x,rey_number,3),0,1, label="Small "*L"R_s"*" asymptotic",linestyle=:dot,   linewidth =3, linecolor=:blue);
else
 plot!(theta_march_plt,x->Ψ_bigr(Δθ*(m-1), x),0,1, label="Large "*L"R_s"*" asymptotic",linestyle=:dot,   linewidth =3, linecolor=:blue);

end
#display(theta_march_plt)

ψ_η_plt = plot(eta[2:N], [ψ_η(n, m, ψ_j, meshsize)/Δη/μ for n=2:N ], title=L"ψ_η",label="θ-marched" , linestyle=:dot, linewidth=2, linecolor=:red, size=(1100,500))
ψ_ηη_plt = plot(eta[2:N], [ψ_ηη(n, m, ψ_j, meshsize)/Δη^2/μ  for n=2:N ], title=L"ψ_{ηη}",label="θ-marched" , linestyle=:dot, linewidth=2,linecolor=:red,  size=(1100,500))
ψ_ηηη_plt = plot(eta[2:N], [ψ_ηηη(n, m, ψ_j, meshsize,  delta_sizes, paramtrs)/Δη^3/μ  for n=2:N ], title=L"ψ_{ηηη}",linewidth=2,linecolor=:red, label="θ-marched" , linestyle=:dot, size=(1100,500))
ψ_ηηηη_plt = plot(eta[2:N], [ψ_ηηηη(n, m, ψ_j, meshsize,  delta_sizes, paramtrs)/Δη^4/μ  for n=2:N ], title=L"ψ_{ηηηη}",linewidth=2,linecolor=:red, label="θ-marched" , linestyle=:dot, size=(1100,500))
ψ_θ_plt = plot(eta[2:N], [(ψ_j[n,m+1]-ψ_j[n,m])/Δθ/μ  for n=2:N ], title=L"ψ_{θ}",label="θ-marched" , linestyle=:dot, linewidth=2, linecolor=:red,size=(1100,500))
ψ_ηηθ_plt = plot(eta[2:N], [(ψ_ηη(n, m+1, ψ_j, meshsize)-ψ_ηη(n, m, ψ_j, meshsize))/(Δθ*Δη^2)/μ  for n=2:N ], title=L"ψ_{ηηθ}",label="θ-marched" , linecolor=:red,linewidth=2, linestyle=:dot, size=(1100,500))
if rey_number < 70.0
     plot!(ψ_η_plt,    x-> Ψ_η_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue, label="Asymptotic", legend=:outertopright)
     plot!(ψ_ηη_plt,   x-> Ψ_ηη_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue,label="Asymptotic",legend=false)
     plot!(ψ_ηηη_plt,  x-> Ψ_ηηη_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue,label="Asymptotic", legend=:outertopright)
     plot!(ψ_ηηηη_plt, x-> Ψ_ηηηη_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue,label="Asymptotic", legend=false)
     plot!(ψ_ηηθ_plt,   x-> Ψ_ηηθ_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue, label="Asymptotic", legend=false)
     plot!(ψ_θ_plt,   x-> Ψ_θ_smallr(Δθ*(m-1), x ,rey_number), 0.0,1.0, linecolor=:blue, label="Asymptotic",legend=:outertopright)
end

plot(ψ_η_plt, ψ_ηη_plt, ψ_ηηη_plt, ψ_ηηηη_plt, ψ_θ_plt, ψ_ηηθ_plt, layout = (3, 2), size=(1100,1000))
xlabel!("η at θ=$(round(Δθ*(m-1),digits=1))\u1D9C")
ylabel!(L"Ψ", yguidefontrotation=-90)

```

```julia
eta_profile =0.6
theta=range(0, stop=pi,length= M+1)

plot((0:M-1)*pi/(M+1),ψ_j[round(Int,(eta_profile)/Δη)+1,1:M], linecolor=:red, label="θ-marched numerical \n steady solution", linestyle=:solid, linewidth=1,legend=:outertopright, size=(1100,500), ylims=(-0.1,0.25))
if rey_number < 60
plot!(x->Ψ_smallr(x,eta_profile,rey_number, 5),0,pi, label="Small "*L"R_s"*" asymptotic", linestyle=:dot, linecolor=:blue, linewidth=3)
end
xticks!(([0:π/6:π;], ["0", "π/6", "π/3", "π/2", "2π/3", "5π/6","π"]))
title!(L"θ"*"-profile at "*L"η= %$eta_profile, \ R_s = %$rey_number")
xlabel!("θ \n"*L"N_η=%$N, M_θ=%$M")
ylabel!(L"Ψ", yguidefontrotation=-90)
```

```julia
plot(theta, log.(matrix_condition_numb), label="Log of Matrix \ncondition number",  size=(1100,500), legend=:outertopright, show=true)
xlabel!("θ")
ylabel!("log(cond(A)")
xticks!(([0:π/6:π;], ["0", "π/6", "π/3", "π/2", "2π/3", "5π/6","π"]))
plot(theta, log.(abs.(matrix_determinant)), label="Log Matrix Determinant",  size=(1100,500), legend=:outertopright)
ylabel!("log(|Det(A)|)")
xlabel!("θ")
xticks!(([0:π/6:π;], ["0", "π/6", "π/3", "π/2", "2π/3", "5π/6","π"]))
```

```julia
if length(matrix_save) > 1
    p=1
    println(matrix_save[p,p:p+3]')
    p=2
    println(matrix_save[p,p-1:p+3]')
    for p =3:N-3
    println(matrix_save[p,p-2:p+2]')
    end
    p=N-2
    println(matrix_save[p,p-2:p+1]')
    p=N-1
    println(matrix_save[p,p-2:p]')
    det(matrix_save)
end
```

```julia
plot(matrix_positive_eigenvals[1:98], linestyle=:dot)
#size(matrix_positive_eigenvals)
#matrix_positive_eigenvals[2]
```

### Finte difference stencil generator
See https://en.wikipedia.org/wiki/Finite_difference_coefficient#Forward_finite_difference

```julia
s = [-3,-2,-1, 0, 1,2] #Stencil points at which finite differences will be calculated
derivs_required = 3
s_N = length(s)
a_vector = zeros(s_N)

stencil_matrix = zeros(s_N,s_N)
for i=1:s_N
    for j=1:s_N
        stencil_matrix[i,j] = s[j]^(i-1)
    end
end

a_vector[derivs_required+1] = 1*factorial(derivs_required)

(stencil_matrix\a_vector)'
```

    1×6 adjoint(::Vector{Float64}) with eltype Float64:
     0.25  -1.75  3.5  -2.5  0.25  0.25

```julia
cont_plt=contour(range(0,stop = 1,length = round(Int,N/2)+1),range(0,stop = pi,length = round(Int,M/2)+1),transpose(ψ_j[1:2:end,1:2:end]), colour=:redsblues, fill=true,levels=50)
yticks!(([0:π/6:π;], ["0", "π/6", "π/3", "π/2", "2π/3", "5π/6","π"]))
plot!(xlabel=L"\eta", ylabel=L"\theta", zlabel=L"\Psi^\mathrm{Sym}", size=(1000,800),camera=(-30,20),xlims=(0,1.0))
title!(L"\Psi^\mathrm{Sym}\  \mathrm{at}\quad R_s = %$rey_number, N_η=%$N, M_θ=%$M")
plot!(yguidefontrotation=-90)
plot!(xtickfont = font(12), ytickfont = font(12), titlefont = font(11))
arrow_scalefactor = 0.1
for n=2:8:N
    η = (n-1)*Δη
    for m =2:8:M
        θ = (m-1)*Δθ
        arrow_x = (ψ_j[n,m+1]-ψ_j[n,m])*arrow_scalefactor/Δθ/μ
        arrow_y = -(ψ_j[n+1,m]-ψ_j[n-1,m])*arrow_scalefactor/(2*Δη)
        if abs(arrow_x) > 0.1
            arrow_x = arrow_x*arrow_scalefactor/abs(arrow_x)
        end
        if abs(arrow_y) > 0.1
            arrow_y = arrow_y*arrow_scalefactor/abs(arrow_y)
        end
        quiver!([(η,θ)],quiver=[(arrow_x,arrow_y)], arrow =0.2, linecolor=:black)
    end
end
display(cont_plt)
```

```julia
a
```

    4-element Vector{Float64}:
      0.0
     -0.5
      0.0
      0.5

```julia

```


# Non-Associative Commutative Algebras for Dynamical Systems

We will consider quadratic dynamical systems, and go to general quasipolynomial differential equations of the form $$\dot{x_i} = x_i\sum_{j=1}^mA_{ij}\prod_{k=1}^nx_k^{B_{jk}}$$

In one dimension we have $$\dot{x}=ax^2+bx+c$$ or in the notation above $$\dot{x}=x(ax+b+c/x)=x\sum_{j=1}^3A_{j}x^{B_j}.$$  With $A_1=a$, $A_2=b$, $A_3=c$ and $B_1=1$, $B_2=0$, $B_3=-1$.

```python
import numpy as np
import sympy as sp
```

```python
a1,a2,a3=sp.Symbol("a1"),sp.Symbol("a2"),sp.Symbol("a3")
b1,b2,b3=sp.Symbol("b1"),sp.Symbol("b2"),sp.Symbol("b3")
A=np.array([a1,a2,a3])
B=np.array([b1,b2,b3])
```

```python
M=np.outer(B,A)
```

```python
print M
```

    [[a1*b1 a2*b1 a3*b1]
     [a1*b2 a2*b2 a3*b2]
     [a1*b3 a2*b3 a3*b3]]

```python

```

    (9, 9)

```python

```


```python
from sympy.physics.mechanics import *
from sympy import symbols, atan, cos, Matrix
```

```python
q = dynamicsymbols('q:4')
qd = dynamicsymbols('q:4', level=1)
l = symbols('l:4')
m = symbols('m:4')
g, t = symbols('g, t')
```

```python
# Compose World Frame
N = ReferenceFrame('N')
B = N.orientnew('B', 'axis', [q[1], N.z])
C = N.orientnew('C', 'axis', [q[2], N.z])
D = N.orientnew('D', 'axis', [q[3], N.z])

B.set_ang_vel(N, qd[1] * N.z)
C.set_ang_vel(N, qd[2] * N.z)
D.set_ang_vel(N, qd[2] * N.z)
```

```python
O = Point('O')
P = O.locatenew('P', l[1] * B.x)
R = P.locatenew('R', l[2] * C.x)
S = R.locatenew('S', l[3] * D.x)
```

```python
O.set_vel(N, qd[0]*N.y)
P.v2pt_theory(O, N, B)
R.v2pt_theory(P, N, C)
S.v2pt_theory(R, N, D)
```

$\displaystyle \dot{q}_{0}\mathbf{\hat{n}_y} + l_{1} \dot{q}_{1}\mathbf{\hat{b}_y} + l_{2} \dot{q}_{2}\mathbf{\hat{c}_y} + l_{3} \dot{q}_{2}\mathbf{\hat{d}_y}$

```python
ParO = Particle('ParO', O, m[0])
ParP = Particle('ParP', P, m[1])
ParR = Particle('ParR', R, m[2])
ParS = Particle('ParS', S, m[3])
```

```python
FL = [(P, m[1] * g * N.x), (R, m[2] * g * N.x),  (S, m[3] * g * N.x)]
```

```python
# Calculate the lagrangian, and form the equations of motion
Lag = Lagrangian(N, ParO, ParP, ParR, ParS)
LM = LagrangesMethod(Lag, q, forcelist=FL, frame=N)
lag_eqs = LM.form_lagranges_equations()
```

```python
lag_eqs.simplify()
lag_eqs
```

$\displaystyle \left[\begin{matrix}- l_{1} m_{1} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} m_{1} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} - l_{1} m_{2} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} m_{2} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} - l_{1} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} - l_{2} m_{2} \sin{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\right)^{2} + l_{2} m_{2} \cos{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} - l_{2} m_{3} \sin{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\right)^{2} + l_{2} m_{3} \cos{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} - l_{3} m_{3} \sin{\left(\operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{2}}{\left(t \right)} \frac{d}{d t} \operatorname{q_{3}}{\left(t \right)} + l_{3} m_{3} \cos{\left(\operatorname{q_{3}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + m_{0} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + m_{1} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + m_{2} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + m_{3} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)}\\l_{1} \left(g m_{1} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} + g m_{2} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} + g m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} \right)} + l_{1} m_{1} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} + l_{1} m_{2} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} + l_{1} m_{3} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} + l_{2} m_{2} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\right)^{2} + l_{2} m_{2} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + l_{2} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\right)^{2} + l_{2} m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + l_{3} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{2}}{\left(t \right)} \frac{d}{d t} \operatorname{q_{3}}{\left(t \right)} + l_{3} m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + m_{1} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + m_{2} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)}\right)\\g l_{2} m_{2} \sin{\left(\operatorname{q_{2}}{\left(t \right)} \right)} + g l_{2} m_{3} \sin{\left(\operatorname{q_{2}}{\left(t \right)} \right)} + g l_{3} m_{3} \sin{\left(\operatorname{q_{3}}{\left(t \right)} \right)} - l_{1} l_{2} m_{2} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} l_{2} m_{2} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} - l_{1} l_{2} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} l_{2} m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} - l_{1} l_{3} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{1}}{\left(t \right)}\right)^{2} + l_{1} l_{3} m_{3} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{1}}{\left(t \right)} \frac{d}{d t} \operatorname{q_{3}}{\left(t \right)} + l_{1} l_{3} m_{3} \cos{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{1}}{\left(t \right)} + l_{2}^{2} m_{2} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + l_{2}^{2} m_{3} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} - l_{2} l_{3} m_{3} \sin{\left(\operatorname{q_{2}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \left(\frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\right)^{2} + 2 l_{2} l_{3} m_{3} \sin{\left(\operatorname{q_{2}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{2}}{\left(t \right)} \frac{d}{d t} \operatorname{q_{3}}{\left(t \right)} + 2 l_{2} l_{3} m_{3} \cos{\left(\operatorname{q_{2}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} + l_{2} m_{2} \cos{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + l_{2} m_{3} \cos{\left(\operatorname{q_{2}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)} + l_{3}^{2} m_{3} \frac{d^{2}}{d t^{2}} \operatorname{q_{2}}{\left(t \right)} - l_{3} m_{3} \sin{\left(\operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{0}}{\left(t \right)} \frac{d}{d t} \operatorname{q_{3}}{\left(t \right)} + l_{3} m_{3} \cos{\left(\operatorname{q_{3}}{\left(t \right)} \right)} \frac{d^{2}}{d t^{2}} \operatorname{q_{0}}{\left(t \right)}\\l_{3} m_{3} \left(- l_{1} \sin{\left(\operatorname{q_{1}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{1}}{\left(t \right)} - l_{2} \sin{\left(\operatorname{q_{2}}{\left(t \right)} - \operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{2}}{\left(t \right)} + \sin{\left(\operatorname{q_{3}}{\left(t \right)} \right)} \frac{d}{d t} \operatorname{q_{0}}{\left(t \right)}\right) \frac{d}{d t} \operatorname{q_{2}}{\left(t \right)}\end{matrix}\right]$

```python

```


```python
import numpy as np
from sympy import *
import matplotlib.pyplot as plt

x = symbols('x')
init_printing(use_unicode=True)

x_range = np.arange(0.001, 0.8, 0.05)

def compute(factor):
    xi = []
    c1 = 1./6
    c2 = factor*c1
    eqs = []
    for phi in x_range:
        c1a= (4.073*np.sqrt(c1)+1.053*c2)*0.5*(3*np.cos(1.5*phi)+np.cos(0.5*phi))
        c2a= 5.28*c2*np.sin(phi)*np.cos(phi)
        eq = c1a*x**(1.5)-x-c2a
        eqs.append(eq)
        # solx = solve(eq, x)
        # xi.append(solx[0])
    xi = [solve(eq, x) for eq in eqs]
    return xi

factors = (1./50, 1./10, 1, 5)

# plt.ion()

for factor in factors:
    plt.plot(x_range, compute(factor))
    # plt.pause(0.01)

# plt.ioff()
plt.show()

```

```python

```


# Asset replacement model with maintenance

**Randall Romero Aguilar, PhD**

This demo is based on the original Matlab demo accompanying the  Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.

Original (Matlab) CompEcon file: **demddp03.m**

Running this file requires the Python version of CompEcon. This can be installed with pip by running

    !pip install compecon --upgrade

Last updated: 2021-Oct-01

## About

Consider the preceding example, but suppose that the productivity of the asset may be
enhanced by performing annual service maintenance. Specifically, at the beginning of each
year, a manufacturer must decide whether to replace the asset with a new one or, if he elects
to keep the asset, whether to service it. An asset that is $a$ years old and has been serviced $s$ times yields a profit contribution $p(a, s)$ up to an age of $n$ years, at which point the asset becomes unsafe and must be replaced by law. The cost of a new asset is $c$, and the cost of servicing an asset is $k$. What replacement-maintenance policy maximizes profits?

## Initial tasks

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from compecon import DDPmodel, gridmake, getindex
```

## Model Parameters
Assume a maximum asset age of 5 years, asset replacement cost $c = 75$, cost of servicing $k = 10$, and annual discount factor $\delta = 0.9$.

```python
maxage  = 5
repcost = 75
mancost = 10
delta   = 0.9
```

### State Space

This is an infinite horizon, deterministic model with time $t$ measured in years. The state
variables

$a \in \{1, 2, 3, \dots, n\}$

$s \in \{0, 1, 2, \dots, n − 1\}$

are the age of the asset in years and the number of servicings it has undergone, respectively.

```python
s1 = np.arange(1, 1 + maxage)   # asset age
s2 = s1 - 1                     # servicings
S  = gridmake(s1,s2)            # combined state grid
S1, S2 = S
n = S1.size                     # total number of states
```

Here, the set of possible asset ages and servicings are generated individually, and then a two-dimensional state grid is constructed by forming their Cartesian product using the CompEcon routine gridmake.

### Action Space
The action variable

$x \in \{\text{no action}, \text{service}, \text{replace}\}$

is the hold-replacement-maintenance decision.

```python
X = np.array(['no action', 'service', 'replace'])  # vector of actions
m = len(X)                               # number of actions
```

### Reward Function
The reward function is
\begin{equation}
f (a, s, x) =
\begin{cases}
p(a, s),         &x = \text{no action}\\
p(0, 0) − c,     &x = \text{service}\\
p(a, s + 1) − k, &x = \text{replace}
\end{cases}
\end{equation}

Assuming a profit contribution $p(a) =
50 − 2.5a −2.5a^2$ that is a function of the asset age $a$ in years.

Here, the rows of the reward matrix, which
correspond to the three admissible decisions (no action, service, replace), are computed
individually.

```python
f = np.zeros((m, n))
q = 50 - 2.5 * S1 - 2.5 * S1 ** 2
f[0] = q * np.minimum(1, 1 - (S1 - S2) / maxage)
f[1] = q * np.minimum(1, 1 - (S1 - S2 - 1) / maxage) - mancost
f[2] = 50 - repcost
```

### State Transition Function
The state transition function is
\begin{equation}
g(a, s, x) =
\begin{cases}
(a + 1, s),     &x = \text{no action}\\
    (1, 0),     &x = \text{service}\\
(a + 1, s + 1), &x = \text{replace}
\end{cases}
\end{equation}

Here, the routine ```getindex``` is used to find the index of the following period’s state.

```python
g = np.empty_like(f)
g[0] = getindex(np.c_[S1 + 1, S2], S)
g[1] = getindex(np.c_[S1 + 1, S2 + 1], S)
g[2] = getindex(np.c_[1, 0], S)
```

# Model Structure

The value of asset of age $a$ that has undergone $s$ servicings satisfies the Bellman equation
\begin{equation}
V(a,s) = \max\{p(a,s) + \delta V(a+1,s),\quad p(a,s+1)−k + \delta V(a+1,s+1),
\quad p(0,0) − c + \delta V(1,0)\}
\end{equation}

where we set $p(n, s) = −\infty$ for all $s$ to enforce replacement of an asset of age $n$. The
Bellman equation asserts that if the manufacturer replaces an asset of age $a$ with servicings
$s$, he earns $p(0,0) − c$ over the coming year and begins the subsequent year with an asset
worth $V(1,0)$; if he services the asset, he earns $p(a, s + 1) − k$ over the coming year
and begins the subsequent year with an asset worth $V(a + 1, s + 1)$. As with the previous
example, the value $V(a, s)$ measures not only the current and future net earnings of the
asset, but also the net earnings of all future assets that replace it.

To solve and simulate this model, use the CompEcon class ```DDPmodel```.

```python
model = DDPmodel(f, g, delta)
model.solve()

```

    A deterministic discrete state, discrete action, dynamic model.
    There are 3 possible actions over 25 possible states

## Analysis

### Simulate Model
The paths are computed by performing q deterministic simulation of 12 years in duration using the ```simulate()``` method.

```python
sinit = 0
nyrs = 12
t = np.arange(nyrs + 1)
spath, xpath = model.simulate(sinit, nyrs)
```

```python
pd.Categorical(X[xpath], categories=X)
```

    ['service', 'service', 'no action', 'replace', 'service', ..., 'service', 'service', 'no action', 'replace', 'service']
    Length: 13
    Categories (3, object): ['no action', 'service', 'replace']

```python
simul = pd.DataFrame({
    'Year': t,
    'Age of Asset': S1[spath],
    'Number of Servicings': S2[spath]}).set_index('Year')

simul['Action'] = pd.Categorical(X[xpath], categories=X)
simul
```

      Age of Asset
      Number of Servicings
      Action

      Year

      0
      1
      0
      service

      1
      2
      1
      service

      2
      3
      2
      no action

      3
      4
      2
      replace

      4
      1
      0
      service

      5
      2
      1
      service

      6
      3
      2
      no action

      7
      4
      2
      replace

      8
      1
      0
      service

      9
      2
      1
      service

      10
      3
      2
      no action

      11
      4
      2
      replace

      12
      1
      0
      service

### Plot State Paths (Age and Servicings) and Action Path
The asset is replaced every four years, and is serviced twice, at the beginning of the second and third years of operation.

```python
fig, axs = plt.subplots(3, 1, sharex=True, figsize=[9,9])
simul['Age of Asset'].plot(marker='o', ax=axs[0])
axs[0].set(title='Age of Asset')

simul['Number of Servicings'].plot(marker='o', ax=axs[1])
axs[1].set(title='Number of Servicings')

simul['Action'].cat.codes.plot(marker='o', ax=axs[2])
axs[2].set(title='Action Path', yticks=range(3), yticklabels=X);
```

_build/html/_sources/notebooks/ddp/03 Asset replacement model with maintenance.ipynb


$$e^x=\sum_{i=0}^\infty \frac{1}{i!}x^i$$

```latex
%%latex

\begin{align}

a = \frac{1}{x}

\begin{equation*}
\mathbf{X} = \left(
\begin{array}{ccc}
x_1 & x_2 & \ldots \\
x_3 & x_4 & \ldots \\
\vdots & \vdots & \ddots
\end{array} \right)
\end{equation*}

\end{align}

```

\begin{align}

a = \frac{1}{x}

\begin{equation*}
\mathbf{X} = \left(
\begin{array}{ccc}
x_1 & x_2 & \ldots \\
x_3 & x_4 & \ldots \\
\vdots & \vdots & \ddots
\end{array} \right)
\end{equation*}

\end{align}

```latex
%%latex

\begin{equation*}
\mathbf{X} = \left(
\begin{array}{ccc}
x_1 & x_2 & \ldots \\
x_3 & x_4 & \ldots \\
\vdots & \vdots & \ddots
\end{array} \right)
\end{equation*}
```

\begin{equation*}
\mathbf{X} = \left(
\begin{array}{ccc}
x_1 & x_2 & \ldots \\
x_3 & x_4 & \ldots \\
\vdots & \vdots & \ddots
\end{array} \right)
\end{equation*}


# Geometry of the thermodynamical phase space

## Introduction

Several areas have been benefited from the use of geometrical tools, gravitation with the Riemannian geometry, classical mechanics with the use of symplectic structures, or the particle physics with the use of convex polytopes in the calculation of scattering amplitudes. It is natural to expect that other areas could be benefited from the use of geometrical methods. In particular, the thermodynamics and the statistical mechanics enjoy this kind of characterization in terms of a geometrical structure widely know in mathematics, the so-called contact structures. This kind of structure corresponds to an odd dimensional analogous of the symplectic structures, structures well known in physics due to his use in classical mechanics in the study of chatotic behaviour and semiclassical aproximations of quantum mechanics.

The symplectic case is of great interest due to his description of the classical phase space $M$. For example, the classical dynamics described by a Hamiltonian function $H(p,q;t)$ can be understood in terms of integral curves of a vector field $X_H$ (curves that have as tangent vectors the vector field $X_H$). In this way, we can say that $X_H$ is defined by the function $H$ and by the symplectic structure of the space. Is in way that we can arrive to the Hamilton's equations of motion from the components of $X_H$ in the local coordinates $(p,q)$ of the symplectic manifold $M$, undestanding the corresponding integral curves as curves where the function $H$ takes constant values. Finally we can make the stament that of system is fully characterized by the Hamiltonian function $H$.

Taking as reference the classical phase space with it's symplectic formalism is natural to expect that the thermodynamics to benefic from being treated in the same footing. This is possible by using a thermodynamical phase space $(p,q,w)$ equiped with a contact structuture.

## Contact geometry essentials

Let $\mathcal{C}$ be a $(2n+1)$ dimensional manifold. It is said that the pair $(\mathcal{C},\lambda)$ with $\lambda$ a 1-form form a contact manifold. $\lambda$ is such that

\begin{equation*}
\lambda \wedge (d\lambda)^n \neq 0,
\end{equation*}

where $\wedge$ denotes the wedge product between diferential forms. This condition can be understood as the requirement of having a non vanishing volumen element defined over the on the manifold.

### Geometry of equilibrium states

### Geometry of non-equilibrium states

## Dynamics from a master equation

### Markovian case

Be $\Gamma$ a finite set of discrete states, $t \in \mathbb{R}$ time, and $\rho(j,t)dt$ the probability of having the state $j \in \Gamma$  in between a time $t$ and $t + dt$ with $\rho: \Gamma \times \mathbb{R} \rightarrow \mathbb{R}_{\geq 0}$ a time dependent probability distribution. Let be  $\rho_\theta^{eq}(j)$ a distribution that can be written in the form

\begin{equation*}
	\rho_\theta^{eq}(j) = \frac{\pi_\theta(j)}{\mathcal{Z(\theta)}},
\end{equation*}

where $\theta \in \Theta \subset \mathbb{R}^n$ is a parameter set, and $\mathcal{Z}: \Theta \rightarrow \mathbb{R}$ is the so called partition function defined such that  $\rho_\theta^{eq}$ be normalized

\begin{equation*}
	\mathcal{Z}(\theta) = \sum_{j\in \Gamma} \pi_\theta(j).
\end{equation*}

Consider the set of master equations with Markovian kernel

\begin{equation*}
	\frac{\partial }{\partial t} \rho(j,t) = \sum_{j' (\neq j)} [w(j|j') \rho(j',t)  -  w(j'|j)\rho(j,t)],
\end{equation*}

where $w(j|j')$ denotes the probability of jumping from a state $j$ to $j'$. Choosing $w_\theta(j|j') = p_\theta^{eq}$ with $p_\theta^{eq}(j) \neq 0$ for all $j \in \Gamma$ we arrive to

\begin{equation}
\frac{\partial }{\partial t} \rho(j,t) = \sum_{j' (\neq j)} [w_\theta(j|j') \rho(j',t)  -  w_\theta(j'|j)\rho(j,t)] = \rho_\theta^{eq}(j) - \rho(j,t).
\end{equation}

The solution to this equiation is given  by

\begin{equation*}
	\rho(j,t) = e^{-t}\rho(j,0) + (1 - e^{-t})\rho_\theta^{eq}(j),
\end{equation*}

expression that allows us to conclude immediately

\begin{equation*}
	\lim_{t \to \infty} \rho(j,t) = \rho_\theta^{eq}.
\end{equation*}

Let $\rho_\theta^{eq}$ denote a family of distributions such that $\pi_\theta(j) = \text{exp}(\theta^a \mathcal{O}_a(j))$ with $\{\mathcal{O_a}\}$ some function set. This family parametrized by $\theta = \{\theta^1,...,\theta^n\} \in \Theta \subset \mathbb{R}^n$ can be written in the following form

\begin{equation*}
	\rho_\theta^{eq}(j) = \text{exp}(\theta^a \mathcal{O}_a - \Psi_{eq}(\theta)),
\end{equation*}

where $\Psi(\theta)$ is such that $	\rho_\theta^{eq}(j)$ is normalized

\begin{equation*}
	\Psi_{eq}(\theta) := ln\Bigg( \sum_{j \in \Gamma} e^{\theta^a \mathcal{O}_a(j)} \Bigg).
\end{equation*}

Through a function, $\Psi(\theta)$ defined for a distribution of this family that not necessarily describes the equilibrium of some thermodynamical system is possible to define the notion of conjugate thermodynamical variables, in particular, the quantities $\theta^a$ and $\langle \mathcal{O}_a \rangle$ define that pair un the following sense

\begin{equation*}
	\eta_a := \langle \mathcal{O}_a \rangle = \frac{\partial \Psi}{\partial \theta^a}, \hspace{0.8 cm} a \in \{1,...,n\}.
\end{equation*}

Now we define the expected values with respect to $\rho(j,t;\theta)$ and $\rho_\theta^{eq}(j)$ as

\begin{equation*}
	\langle \mathcal{O}_a \rangle_\theta (t) := \sum_{j \in \Gamma} \mathcal{O}_a (j) \rho(j,t;\theta) \hspace{0.3 cm} \text{y} \hspace{0.3 cm} \langle \mathcal{O}_a \rangle^{eq} := \sum_{j \in \Gamma} \mathcal{O}_a (j) \rho_\theta^{eq}(j)
\end{equation*}

respectivaly. The definition of $\Psi$ can be extended for non equilibrium states in the following way

\begin{equation*}
	\Psi(\theta,t) := \Bigg( \frac{1}{J_0} \sum_{j \in \Gamma} \frac{\rho(j,t;\theta)}{\rho_\theta^{eq}(j)} \Bigg) \Psi_{eq}(\theta), \hspace{0.3 cm} \text{with} \hspace{0.3 cm} J_0 := \sum_{j' \in \Gamma} 1.
\end{equation*}

Finally, using the master equation and the definitions for the expected values it can be found that the dynamics that follow the conjugate thermodynamical quantities $\theta_a$ y $\langle \mathcal{O}_a \rangle$ and the function $\Psi$ asocieted to $\rho(j,t;\theta)$ is given by the following set of equations

\begin{equation}
	\frac{d}{dt} \theta^a = 0, \hspace{0.3 cm} \frac{d}{dt} \langle \mathcal{O}_a \rangle_\theta = - \langle \mathcal{O}_a \rangle_\theta + \frac{\partial \Psi_{eq}}{\partial \theta^a}, \hspace{0.3cm} \frac{d}{dt} \Psi = -\Psi + \Psi_{eq}
\end{equation}

### Non-Markovian case
Coming soon...

```python
import numpy as np

a = [10,10,10]
```

```python
print a
```

    [10, 10, 10]

```python

```

Contact-Dynamics/Geometry of the thermodynamical phase space.ipynb


# Gaussian Process Regression and Classification with Elliptical Slice Sampling

[Elliptical slice sampling](https://arxiv.org/abs/1001.0175) is a variant of slice sampling that allows sampling from distributions with multivariate Gaussian prior and arbitrary likelihood. It is generally about as fast as regular slice sampling, mixes well even when the prior covariance might otherwise induce a strong dependence between samples, and does not depend on any tuning parameters. It can be useful when working with Gaussian processes, in which a multivariate Gaussian prior is used to impose a covariance structure on some latent function.

This notebook provides examples of how to use PyMC3's elliptical slice sampler to perform Gaussian process regression and classification. Since the focus of these examples are to show how to of elliptical slice sampling to sample from the posterior rather than to show how to fit the covariance kernel parameters, we assume that the kernel parameters are known.

```python
import pymc3 as pm
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import theano.tensor as tt

sns.set(style='white', palette='deep', color_codes=True)

%matplotlib inline
```

## Gaussian Process Regression

In Gaussian process regression, the prior $f$ is a multivariate normal with mean zero and covariance matrix $K$, and the likelihood is a factored normal (or, equivalently, a multivariate normal with diagonal covariance) with mean $f$ and variance $\sigma^2_n$:

\begin{equation}
f \sim N(\boldsymbol{0}, K) \\
L(y | f, \sigma^2_n) = \Pi_n N(f_n, \sigma^2_n)
\end{equation}

### Generate some example data

We generate some data from Gaussian process at 30 random points in $[0, 3]$ and interpolate the function's value in this interval.

```python
np.random.seed(1)

# Number of training points
n = 30
X0 = np.sort(3 * np.random.rand(n))[:, None]

# Number of points at which to interpolate
m = 100
X = np.linspace(0, 3, m)[:, None]

# Covariance kernel parameters
noise = 0.1
lengthscale = 0.3
f_scale = 1

cov = f_scale * pm.gp.cov.ExpQuad(1, lengthscale)
K = cov(X0)
K_s = cov(X0, X)
K_noise = K + noise * tt.eye(n)

# Add very slight perturbation to the covariance matrix diagonal to improve numerical stability
K_stable = K + 1e-12 * tt.eye(n)

# Observed data
f = np.random.multivariate_normal(mean=np.zeros(n), cov=K_noise.eval())
```

### Examine actual posterior distribution

The posterior is analytically tractable so we can compute the posterior mean explicitly. Rather than computing the inverse of the covariance matrix `K`, we use the numerically stable calculation described Algorithm 2.1 in the book "Gaussian Processes for Machine Learning" (2006) by Rasmussen and Williams, which is [available online for free](http://www.gaussianprocess.org/gpml/).

```python
fig, ax = plt.subplots(figsize=(14, 6));
ax.scatter(X0, f, s=40, color='b', label='True points');

# Analytically compute posterior mean
L = np.linalg.cholesky(K_noise.eval())
alpha = np.linalg.solve(L.T, np.linalg.solve(L, f))
post_mean = np.dot(K_s.T.eval(), alpha)

ax.plot(X, post_mean, color='g', alpha=0.8, label='Posterior mean');

ax.set_xlim(0, 3);
ax.set_ylim(-2, 2);
ax.legend();
```

### Sample from posterior distribution

```python
with pm.Model() as model:
    # The actual distribution of f_sample doesn't matter as long as the shape is right since it's only used
    # as a dummy variable for slice sampling with the given prior
    f_sample = pm.Flat('f_sample', shape=(n, ))

    # Likelihood
    y = pm.MvNormal('y', observed=f, mu=f_sample, cov=noise * tt.eye(n), shape=n)

    # Interpolate function values using noisy covariance matrix
    L = tt.slinalg.cholesky(K_noise)
    f_pred = pm.Deterministic('f_pred', tt.dot(K_s.T, tt.slinalg.solve(L.T, tt.slinalg.solve(L, f_sample))))

    # Use elliptical slice sampling
    ess_step = pm.EllipticalSlice(vars=[f_sample], prior_cov=K_stable)
    trace = pm.sample(5000, start=model.test_point, step=[ess_step], progressbar=False, random_seed=1)
```

### Evaluate posterior fit

The posterior samples are consistent with the analytically derived posterior and behaves how one would expect–narrower near areas with lots of observations and wider in areas with more uncertainty.

```python
fig, ax = plt.subplots(figsize=(14, 6));
for idx in np.random.randint(4000, 5000, 500):
    ax.plot(X, trace['f_pred'][idx],  alpha=0.02, color='navy')
ax.scatter(X0, f, s=40, color='k', label='True points');
ax.plot(X, post_mean, color='g', alpha=0.8, label='Posterior mean');
ax.legend();
ax.set_xlim(0, 3);
ax.set_ylim(-2, 2);
```

## Gaussian Process Classification

In Gaussian process classification, the likelihood is not normal and thus the posterior is not analytically tractable. The prior is again a multivariate normal with covariance matrix $K$, and the likelihood is the standard likelihood for logistic regression:

\begin{equation}
L(y | f) = \Pi_n \sigma(y_n, f_n)
\end{equation}

### Generate some example data

We generate random samples from a Gaussian process, assign any points greater than zero to a "positive" class, and assign all other points to a "negative" class.

```python
np.random.seed(5)
f = np.random.multivariate_normal(mean=np.zeros(n), cov=K_stable.eval())

# Separate data into positive and negative classes
f[f > 0] = 1
f[f <= 0] = 0
```

```python
fig, ax = plt.subplots(figsize=(14, 6));
ax.scatter(X0, np.ma.masked_where(f == 0, f), color='b', label='Positive Observations');
ax.scatter(X0, np.ma.masked_where(f == 1, f), color='r', label='Negative Observations');
ax.legend(loc='lower right');
ax.set_xlim(-0.1, 3.1);
ax.set_ylim(-0.2, 1.2);
```

### Sample from posterior distribution

```python
with pm.Model() as model:
    # Again, f_sample is just a dummy variable
    f_sample = pm.Flat('f_sample', shape=n)
    f_transform = pm.invlogit(f_sample)

    # Binomial likelihood
    y = pm.Binomial('y', observed=f, n=np.ones(n), p=f_transform, shape=n)

    # Interpolate function values using noiseless covariance matrix
    L = tt.slinalg.cholesky(K_stable)
    f_pred = pm.Deterministic('f_pred', tt.dot(K_s.T, tt.slinalg.solve(L.T, tt.slinalg.solve(L, f_transform))))

    # Use elliptical slice sampling
    ess_step = pm.EllipticalSlice(vars=[f_sample], prior_cov=K_stable)
    trace = pm.sample(5000, start=model.test_point, step=[ess_step], progressbar=False, random_seed=1)
```

### Evaluate posterior fit

The posterior looks good, though the fit is, unsurprisingly, erratic outside the range of the observed data.

```python
fig, ax = plt.subplots(figsize=(14, 6));
for idx in np.random.randint(4000, 5000, 500):
    ax.plot(X, trace['f_pred'][idx],  alpha=0.04, color='navy')
ax.scatter(X0, f, s=40, color='k');
ax.set_xlim(0, 3);
ax.set_ylim(-0.1, 1.1);
```


```python
import numpy as np
import random
from sympy import *
```

### Chapter 7

#### Flow Augmentation Algorithm

```python
#G=[['A', 'C', 8], ['A', 'B', 6], ['A', 'D', 3], ['B', 'C', 5], ['B', 'D', 6], ['D', 'E', 9], ['C', 'E', 4]]
#G=[['A', 'B', 5], ['A', 'C', 7], ['A', 'D', 4], ['D', 'E', 4], ['B', 'C', 1], ['C', 'D', 2], ['C', 'E', 5], ['B', 'F', 3], ['C', 'F', 4], ['E', 'F', 1], ['F', 'G', 9], ['E', 'G', 6]]

#Ex 7.2, 27
G=[['A', 'B', 4], ['B', 'D', 2], ['A', 'D', 9], ['A', 'C', 6], ['C', 'D', 2], ['B', 'E', 3], ['C', 'F', 4], ['D', 'E', 4], ['D', 'F', 4], ['E', 'G', 7], ['D', 'G', 3], ['F', 'G', 9]]

def FAA(G):
    G1, G2=[], []
    for i in G:
        G1+=[i[:-1], ]
        G2+=[i[-1], ]
    G3=[0]*len(G1)

    #determine the source and the sink
    V=list(set(flatten(G1)))
    V.sort()

    In=0
    source=''
    for v in V:
        for i in G1:
            if i[1]==v:In+=1
        if In==0:
            source=v
            break
        else: In=0

    Out=0
    sink=''
    for v in V:
        for i in G1:
            if i[0]==v:Out+=1
        if Out==0:
            sink=v
            break
        else: Out=0

    L={source:['source', '+', oo]}
    l=[source]
    l1=l[:]
    S=[]

    while True:
        while len(l)!=len(S):
            for v in l:
                if v not in S:
                    for w in V:
                        if w not in L:
                            if [v, w] in G1:
                                ind=G1.index([v, w])
                                if G3[ind]<G2[ind]:L[w]=[v, '+', min(L[v][2], G2[ind]-G3[ind])]
                            elif [w, v] in G1:
                                ind=G1.index([w, v])
                                if G3[ind]>0:L[w]=[v, '-', min(L[v][2], G3[ind])]
                    S+=[v, ]

            for i in L.keys():
                if i not in l: l+=[i,]
            if sink in l: break

        if sink not in l:
            break
        else:
            v=sink

            a=L[v][2]
            while v!=source:
                u=L[v][0]
                if L[v][1]=='+':
                    ind=G1.index([u, v])
                    G3[ind]+=a
                elif L[v][1]=='-':
                    ind=G1.index([v, u])
                    G3[ind]-=a
                v=u
            L={source:['source', '+', oo]}
            l=[source]
            S=[]

    return [[G1[i][0], G1[i][1], G2[i], G3[i]] for i in range(len(G1))]
FAA(G)
```

    [['A', 'B', 4, 4],
     ['B', 'D', 2, 1],
     ['A', 'D', 9, 9],
     ['A', 'C', 6, 5],
     ['C', 'D', 2, 1],
     ['B', 'E', 3, 3],
     ['C', 'F', 4, 4],
     ['D', 'E', 4, 4],
     ['D', 'F', 4, 4],
     ['E', 'G', 7, 7],
     ['D', 'G', 3, 3],
     ['F', 'G', 9, 8]]

```python

```