# Tutorial-IllinoisGRMHD: harm_utoprim_2d.c

## Authors: Leo Werneck & Zach Etienne

**This module is currently under development**

## In this tutorial module we explain the conservative-to-primitive algorithm used by `HARM`. This module will likely be absorbed by another one once we finish documenting the code.

### Required and recommended citations:

* **(Required)** Etienne, Z. B., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L. IllinoisGRMHD: an open-source, user-friendly GRMHD code for dynamical spacetimes. Class. Quantum Grav. 32 (2015) 175009. ([arxiv:1501.07276](http://arxiv.org/abs/1501.07276)).
* **(Required)** Noble, S. C., Gammie, C. F., McKinney, J. C., Del Zanna, L. Primitive Variable Solvers for Conservative General Relativistic Magnetohydrodynamics. Astrophysical Journal, 641, 626 (2006) ([astro-ph/0512420](https://arxiv.org/abs/astro-ph/0512420)).
* **(Recommended)** Del Zanna, L., Bucciantini N., Londrillo, P. An efficient shock-capturing central-type scheme for multidimensional relativistic flows - II. Magnetohydrodynamics. A&A 400 (2) 397-413 (2003). DOI: 10.1051/0004-6361:20021641 ([astro-ph/0210618](https://arxiv.org/abs/astro-ph/0210618)).

# Table of Contents
$$\label{toc}$$

This module is organized as follows

0. [Step 0](#src_dir): **Source directory creation**
1. [Step 1](#introduction): **Introduction**
1. [Step 2](#harm_utoprim_2d__c__eos_indep): **EOS independent routines**
    1. [Step 2.a](#utoprim_2d): *The `Utoprim_2d()` function*
        1. [Step 2.a.i](#utoprim_2d__bi_and_alpha): Setting $B^{i}_{\rm HARM}$ and $\alpha$
        1. [Step 2.a.ii](#utoprim_2d__converting):  Preparing the variables to be used by the `Utoprim_new_body()` function
    1. [Step 2.b](#utoprim_new_body): *The `Utoprim_new_body()` function*
        1. [Step 2.b.i](#utoprim_new_body__basic_quantities): Computing basic quantities
        1. [Step 2.b.ii](#utoprim_new_body__wlast): Determining $W$ from the previous iteration, $W_{\rm last}$
        1. [Step 2.b.iii](#utoprim_new_body__vsqlast_and_recompute_w_and_vsq): Compute $v^{2}_{\rm last}$, then update $v^{2}$ and $W$
        1. [Step 2.b.iv](#utoprim_new_body__compute_prims): Computing the primitive variables
    1. [Step 2.c](#vsq_calc): *The `vsq_calc()` function*
    1. [Step 2.d](#x1_of_x0): *The `x1_of_x0()` function*
    1. [Step 2.e](#validate_x): *The `validate_x()` function*
    1. [Step 2.f](#general_newton_raphson): *The `general_newton_raphson()` function*
    1. [Step 2.g](#func_vsq): *The `func_vsq()` function*
1. [Step 3](#harm_utoprim_2d__c__eos_dep): **EOS dependent routines**
    1. [Step 3.a](#pressure_w_vsq): *The `pressure_W_vsq()` function*
    1. [Step 3.b](#dpdw_calc_vsq): *The `dpdW_calc_vsq()` function*
    1. [Step 3.c](#dpdvsq_calc): *The `dpdvsq_calc()` function*
        1. [Step 3.c.i](#dpdvsq_calc__basic_quantities): Setting basic quantities and computing $P_{\rm cold}$ and $\epsilon_{\rm cold}$
        1. [Step 3.c.ii](#dpdvsq_calc__dpcolddvsq): Computing $\frac{\partial P_{\rm cold}}{\partial\left(v^{2}\right)}$
        1. [Step 3.c.iii](#dpdvsq_calc__depscolddvsq): Computing $\frac{\partial \epsilon_{\rm cold}}{\partial\left(v^{2}\right)}$
        1. [Step 3.c.iv](#dpdvsq_calc__dpdvsq): Computing $\frac{\partial p_{\rm hybrid}}{\partial\left(v^{2}\right)}$
1. [Step 4](#code_validation): **Code validation**
1. [Step 5](#latex_pdf_output): **Output this notebook to $\LaTeX$-formatted PDF file**

# Step 0: Source directory creation \[Back to [top](#toc)\]
$$\label{src_dir}$$

We will now use the [cmdline_helper.py NRPy+ module](Tutorial-Tutorial-cmdline_helper.ipynb) to create the source directory within the `IllinoisGRMHD` NRPy+ directory, if it does not exist yet.

```python
# Step 0: Creation of the IllinoisGRMHD source directory
# Step 0a: Add NRPy's directory to the path
# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
import os,sys
nrpy_dir_path = os.path.join("..","..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Step 0b: Load up cmdline_helper and create the directory
import cmdline_helper as cmd
IGM_src_dir_path = os.path.join("..","src")
cmd.mkdir(IGM_src_dir_path)

# Step 0c: Create the output file path
outfile_path__harm_utoprim_2d__c = os.path.join(IGM_src_dir_path,"harm_utoprim_2d.c")
```

# Step 1: Introduction \[Back to [top](#toc)\]
$$\label{introduction}$$

Comment on license: `HARM` uses GPL, while `IllinoisGRMHD` uses BSD.

# Step 2: EOS independent routines \[Back to [top](#toc)\]
$$\label{harm_utoprim_2d__c__eos_indep}$$

Let us now start documenting the `harm_utoprim_2d.c`, which is a part of the `Harm` code. Our main reference throughout this discussion will be the required citation [Noble *et al.* (2006)](https://arxiv.org/abs/astro-ph/0512420).

We will start with the code's required preamble.

```python
%%writefile $outfile_path__harm_utoprim_2d__c
#ifndef __HARM_UTOPRIM_2D__C__
#define __HARM_UTOPRIM_2D__C__
/***********************************************************************************
    Copyright 2006 Charles F. Gammie, Jonathan C. McKinney, Scott C. Noble,
                   Gabor Toth, and Luca Del Zanna

                        HARM  version 1.0   (released May 1, 2006)

    This file is part of HARM.  HARM is a program that solves hyperbolic
    partial differential equations in conservative form using high-resolution
    shock-capturing techniques.  This version of HARM has been configured to
    solve the relativistic magnetohydrodynamic equations of motion on a
    stationary black hole spacetime in Kerr-Schild coordinates to evolve
    an accretion disk model.

    You are morally obligated to cite the following two papers in his/her
    scientific literature that results from use of any part of HARM:

    [1] Gammie, C. F., McKinney, J. C., \& Toth, G.\ 2003,
        Astrophysical Journal, 589, 444.

    [2] Noble, S. C., Gammie, C. F., McKinney, J. C., \& Del Zanna, L. \ 2006,
        Astrophysical Journal, 641, 626.

    Further, we strongly encourage you to obtain the latest version of
    HARM directly from our distribution website:
    http://rainman.astro.uiuc.edu/codelib/

    HARM is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    HARM is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with HARM; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

***********************************************************************************/

/*************************************************************************************/
/*************************************************************************************/
/*************************************************************************************

utoprim_2d.c:
---------------

    Uses the 2D method:
       -- solves for two independent variables (W,v^2) via a 2D
          Newton-Raphson method
       -- can be used (in principle) with a general equation of state.

  -- Currently returns with an error state (>0) if a negative rest-mass
      density or internal energy density is calculated.  You may want
      to change this aspect of the code so that it still calculates the
      velocity and so that you can floor the densities.  If you want to
      change this aspect of the code please comment out the "return(retval)"
      statement after "retval = 5;" statement in Utoprim_new_body();

******************************************************************************/

static const int NEWT_DIM=2;

// Declarations:
static CCTK_REAL vsq_calc(CCTK_REAL W,CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
static int Utoprim_new_body(eos_struct eos, CCTK_REAL U[], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM], CCTK_REAL gdet,  CCTK_REAL prim[],long &n_iter);
static int general_newton_raphson( eos_struct eos, CCTK_REAL x[], int n, long &n_iter, void (*funcd) (eos_struct, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *, CCTK_REAL *, int,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &),CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
static void func_vsq( eos_struct eos, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *f, CCTK_REAL *df, int n,CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
static CCTK_REAL x1_of_x0(CCTK_REAL x0, CCTK_REAL &Bsq, CCTK_REAL &QdotBsq, CCTK_REAL &Qtsq, CCTK_REAL &Qdotn, CCTK_REAL &D ) ;
static CCTK_REAL pressure_W_vsq(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D) ;
static CCTK_REAL dpdW_calc_vsq(CCTK_REAL W, CCTK_REAL vsq);
static CCTK_REAL dpdvsq_calc(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D);

/**********************************************************************/
/******************************************************************

  Utoprim_2d():

  -- Driver for new prim. var. solver.  The driver just translates
     between the two sets of definitions for U and P.  The user may
     wish to alter the translation as they see fit.  Note that Greek
     indices run 0,1,2,3 and Latin indices run 1,2,3 (spatial only).

             /     rho u^t     \
         U = | T^t_t + rho u^t |  sqrt(-det(g_{\mu\nu}))
             |      T^t_i      |
             \       B^i       /

             /     rho     \
         P = |     uu      |
             | \tilde{u}^i |
             \     B^i     /

   Arguments:
       U[NPR]           = conserved variables (current values on input/output);
       gcov[NDIM][NDIM] = covariant form of the metric ;
       gcon[NDIM][NDIM] = contravariant form of the metric ;
       gdet             = sqrt( - determinant of the metric) ;
       prim[NPR]        = primitive variables (guess on input, calculated values on
                                                output if there are no problems);

   -- NOTE: for those using this routine for special relativistic MHD and are
            unfamiliar with metrics, merely set
              gcov = gcon = diag(-1,1,1,1)  and gdet = 1.  ;

******************************************************************/
```

    Writing ../src/harm_utoprim_2d.c

## Step 2.a: The `Utoprim_2d()` function \[Back to [top](#toc)\]
$$\label{utoprim_2d}$$

The `Utoprim_2d()` function is the driver function of the `HARM` conservative-to-primitive algorithm. We remind you from the definitions of primitive and conservative variables used in the code:

$$
\begin{align}
\boldsymbol{P}_{\rm HARM} &= \left\{\rho_{b},u,\tilde{u}^{i},B^{i}_{\rm HARM}\right\}\ ,\\
\boldsymbol{C}_{\rm HARM} &= \left\{\sqrt{-g}\rho_{b}u^{0},\sqrt{-g}\left(T^{0}_{\ 0}+\rho_{b}u^{0}\right),\sqrt{-g}T^{0}_{\ i},\sqrt{-g}B^{i}_{\rm HARM}\right\}\ .
\end{align}
$$

### Step 2.a.i: Setting $B^{i}_{\rm HARM}$ and $\alpha$ \[Back to [top](#toc)\]
$$\label{utoprim_2d__bi_and_alpha}$$

Let

$$
\tilde{B}^{i}_{\rm HARM} \equiv \sqrt{-g}B^{i}_{\rm HARM}\ .
$$

The code starts by relating

$$
\boxed{B^{i}_{\rm HARM} = \frac{\tilde{B}^{i}_{\rm HARM}}{\sqrt{-g}}}\ ,
$$

and setting

$$
\boxed{\alpha = \frac{1}{\sqrt{-g^{00}}}} \ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

int Utoprim_2d(eos_struct eos, CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM],
               CCTK_REAL gdet, CCTK_REAL prim[NPR], long &n_iter)
{

  CCTK_REAL U_tmp[NPR], prim_tmp[NPR];
  int i, ret;
  CCTK_REAL alpha;

  if( U[0] <= 0. ) {
    return(-100);
  }

  /* First update the primitive B-fields */
  for(i = BCON1; i <= BCON3; i++) prim[i] = U[i] / gdet ;

  /* Set the geometry variables: */
  alpha = 1.0/sqrt(-gcon[0][0]);
```

    Appending to ../src/harm_utoprim_2d.c

### Step 2.a.ii: Preparing the variables to be used by the `Utoprim_new_body()` function \[Back to [top](#toc)\]
$$\label{utoprim_2d__converting}$$

The conservative-to-primitive algorithm uses the `Utoprim_new_body()` function. However, this function assumes a *different* set of primitive/conservative variables. Thus, we must perform the proper conversion. First, let us ease on the notation a bit by defining:

$$
\boldsymbol{C} \equiv \left\{\rho_{\star},u_{\star},\tilde{S}_{i},\tilde{B}^{i}_{\rm HARM}\right\} \equiv \left\{\sqrt{-g}\rho_{b}u^{0},\sqrt{-g}\left(T^{0}_{\ 0}+\rho_{b}u^{0}\right),\sqrt{-g}T^{0}_{\ i},\sqrt{-g}B^{i}_{\rm HARM}\right\}\ .
$$

Below we list the main differences in the conservative variables:

|              `Utoprim_2d()`              |                            `Utoprim_new_body()`                           |
|------------------------------------------|---------------------------------------------------------------------------|
| $\color{blue}{\textbf{Conservatives}}$   | $\color{red}{\textbf{Conservatives}}$                                     |
| $\color{blue}{\rho_{\star}}$             | $\color{red}{\frac{\alpha}{\sqrt{-g}}\rho_{\star}}$                       |
| $\color{blue}{u_{\star}}$                | $\color{red}{\frac{\alpha}{\sqrt{-g}}\left(u_{\star}-\rho_{\star}\right)}$|
| $\color{blue}{\tilde{S}_{i}}$            | $\color{red}{\frac{\alpha}{\sqrt{-g}}\tilde{S}_{i}}$                      |
| $\color{blue}{\tilde{B}^{i}_{\rm HARM}}$ | $\color{red}{\frac{\alpha}{\sqrt{-g}}\tilde{B}^{i}_{\rm HARM}}$           |

These are necessary conversions because while `Utoprim_2d()` assumes the set of conservatives above, `Utoprim_new_body()` assumes

$$
\left\{\gamma\rho_{b},\alpha T^{0}_{\ \ 0}, \alpha T^{0}_{\ \ i}, \alpha B^{i}_{\rm HARM}\right\}\ .
$$

Let us first pause to understand the table above. From definition (15) in [Noble *et al.* (2006)](https://arxiv.org/abs/astro-ph/0512420) and the discussion just below it, we know that $\gamma = \alpha u^{0}$. Thus

$$
\rho_{\star} = \sqrt{-g}\rho_{b}u^{0} = \sqrt{-g}\left(\frac{\gamma}{\alpha}\rho_{b}\right)\implies\boxed{\gamma \rho_{b} = \frac{\alpha}{\sqrt{-g}}\rho_{\star}}\ .
$$

Then we have

$$
u_{\star} = \sqrt{-g}\left(T^{0}_{\ \ 0} + \rho_{b}u^{0}\right)= \sqrt{-g}\left(T^{0}_{\ \ 0} + \frac{\rho_{\star}}{\sqrt{-g}}\right) = \sqrt{-g}T^{0}_{\ \ 0} + \rho_{\star} \implies \boxed{\alpha T^{0}_{\ \ 0} = \frac{\alpha}{\sqrt{-g}}\left(u_{\star}-\rho_{\star}\right)}\ .
$$

The other two relations are more straightforward. We have

$$
\tilde{S}_{i} = \sqrt{-g}T^{0}_{\ \ i} \implies \boxed{\alpha T^{0}_{\ \ i} = \frac{\alpha}{\sqrt{-g}}\tilde{S}_{i}}\ ,
$$

and

$$
\tilde{B}^{i}_{\rm HARM} = \sqrt{-g}B^{i}_{\rm HARM}\implies \boxed{\alpha B^{i}_{\rm HARM} = \frac{\alpha}{\sqrt{-g}}\tilde{B}^{i}_{\rm HARM}}\ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* Transform the CONSERVED variables into the new system */
  U_tmp[RHO] = alpha * U[RHO] / gdet;
  U_tmp[UU]  = alpha * (U[UU] - U[RHO])  / gdet ;
  for( i = UTCON1; i <= UTCON3; i++ ) {
    U_tmp[i] = alpha * U[i] / gdet ;
  }
  for( i = BCON1; i <= BCON3; i++ ) {
    U_tmp[i] = alpha * U[i] / gdet ;
  }
```

    Appending to ../src/harm_utoprim_2d.c

Below we list the necessary transformations on the primitive variables:

|           `Utoprim_2d()`            |          `Utoprim_new_body()`          |
|-------------------------------------|----------------------------------------|
| $\color{blue}{\textbf{Primitives}}$ |   $\color{red}{\textbf{Primitives}}$   |
|       $\color{blue}{\rho_{b}}$      |         $\color{red}{\rho_{b}}$        |
|         $\color{blue}{u}$           |            $\color{red}{u}$            |
|   $\color{blue}{\tilde{u}^{i}}$     |      $\color{red}{\tilde{u}^{i}}$      |
|  $\color{blue}{B^{i}_{\rm HARM}}$   | $\color{red}{\alpha B^{i}_{\rm HARM}}$ |

After this slight modification we call the `Utoprim_new_body()` function. If it returns without errors, than the variables ${\rm prim\_tmp}$ will now contain the values of the primitives. We then update the ${\rm prim}$ variables with these newly computed values.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* Transform the PRIMITIVE variables into the new system */
  for( i = 0; i < BCON1; i++ ) {
    prim_tmp[i] = prim[i];
  }
  for( i = BCON1; i <= BCON3; i++ ) {
    prim_tmp[i] = alpha*prim[i];
  }

  ret = Utoprim_new_body(eos, U_tmp, gcov, gcon, gdet, prim_tmp,n_iter);

  /* Transform new primitive variables back if there was no problem : */
  if( ret == 0 || ret == 5 || ret==101 ) {
    for( i = 0; i < BCON1; i++ ) {
      prim[i] = prim_tmp[i];
    }
  }

  return( ret ) ;

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.b: The `Utoprim_new_body()` function \[Back to [top](#toc)\]
$$\label{utoprim_new_body}$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/**********************************************************************************

  Utoprim_new_body():

     -- Attempt an inversion from U to prim using the initial guess prim.

     -- This is the main routine that calculates auxiliary quantities for the
        Newton-Raphson routine.

  -- assumes that
             /   rho gamma   \
         U = | alpha T^t_\mu |
             \   alpha B^i   /

             /     rho     \
      prim = |     uu      |
             | \tilde{u}^i |
             \  alpha B^i  /

return:  (i*100 + j)  where
         i = 0 ->  Newton-Raphson solver either was not called (yet or not used)
                   or returned successfully;
             1 ->  Newton-Raphson solver did not converge to a solution with the
                   given tolerances;
             2 ->  Newton-Raphson procedure encountered a numerical divergence
                   (occurrence of "nan" or "+/-inf" ;

         j = 0 -> success
             1 -> failure: some sort of failure in Newton-Raphson;
             2 -> failure: utsq<0 w/ initial p[] guess;
             3 -> failure: W<0 or W>W_TOO_BIG
             4 -> failure: v^2 > 1
             5 -> failure: rho,uu <= 0 ;

**********************************************************************************/

static int Utoprim_new_body(eos_struct eos, CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM],
                            CCTK_REAL gcon[NDIM][NDIM], CCTK_REAL gdet,  CCTK_REAL prim[NPR], long &n_iter)
{

  CCTK_REAL x_2d[NEWT_DIM];
  CCTK_REAL QdotB,Bcon[NDIM],Bcov[NDIM],Qcov[NDIM],Qcon[NDIM],ncov[NDIM],ncon[NDIM],Qsq,Qtcon[NDIM];
  CCTK_REAL rho0,u,p,w,gammasq,gamma,gtmp,W_last,W,utsq,vsq;
  int i,j, n, retval, i_increase;

  n = NEWT_DIM ;

  // Assume ok initially:
  retval = 0;
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.b.i: Computing basic quantities \[Back to [top](#toc)\]
$$\label{utoprim_new_body__basic_quantities}$$

We start by computing basic quantities from the input variables. Notice that this conservative-to-primitive algorithm does not need to update the magnetic field, thus

$$
\boxed{B_{\rm prim}^{i} = B_{\rm conserv}^{i}}\ .
$$

Since they are both equal, we will not distinguish between prim and conserv in what follows. We also set $B^{0} = 0$. Then we define

$$
\boxed{Q_{\mu} \equiv \alpha T^{0}_{\ \ \mu}}\ .
$$

From these, the following quantities are then computed:

$$
\boxed{
\begin{align}
B_{i} &= g_{i\mu}B^{\mu}\\
Q^{\mu} &= g^{\mu\nu}Q_{\nu}\\
B^{2} &= B_{i}B^{i}\\
Q\cdot B &= Q_{\mu}B^{\mu}\\
\left(Q\cdot B\right)^{2} &= \left(Q\cdot B\right)\left(Q\cdot B\right)\\
n_{\mu} &= \left(-\alpha,0,0,0\right)\\
n^{\mu} &= g^{\mu\nu}n_{\nu}\\
\left(Q\cdot n\right) &= Q^{\mu}n_{\mu}\\
Q^{2} &= Q_{\mu}Q^{\mu}\\
\tilde{Q}^{2} &= Q^{2} + \left(Q\cdot n\right)\left(Q\cdot n\right)\\
D &\equiv \gamma \rho_{b}
\end{align}
}\ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  for(i = BCON1; i <= BCON3; i++) prim[i] = U[i] ;

  // Calculate various scalars (Q.B, Q^2, etc)  from the conserved variables:
  Bcon[0] = 0. ;
  for(i=1;i<4;i++) Bcon[i] = U[BCON1+i-1] ;

  lower_g(Bcon,gcov,Bcov) ;

  for(i=0;i<4;i++) Qcov[i] = U[QCOV0+i] ;
  raise_g(Qcov,gcon,Qcon) ;

  CCTK_REAL Bsq = 0. ;
  for(i=1;i<4;i++) Bsq += Bcon[i]*Bcov[i] ;

  QdotB = 0. ;
  for(i=0;i<4;i++) QdotB += Qcov[i]*Bcon[i] ;
  CCTK_REAL QdotBsq = QdotB*QdotB ;

  ncov_calc(gcon,ncov) ;
  // FIXME: The exact form of n^{\mu} can be found
  //        in eq. (2.116) and implementing it
  //        directly is a lot more efficient than
  //        performing n^{\mu} = g^{\mu\nu}n_{nu}
  raise_g(ncov,gcon,ncon);

  CCTK_REAL Qdotn = Qcon[0]*ncov[0] ;

  Qsq = 0. ;
  for(i=0;i<4;i++) Qsq += Qcov[i]*Qcon[i] ;

  CCTK_REAL Qtsq = Qsq + Qdotn*Qdotn ;

  CCTK_REAL D = U[RHO] ;
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.b.ii: Determining $W$ from the previous iteration, $W_{\rm last}$ \[Back to [top](#toc)\]
$$\label{utoprim_new_body__wlast}$$

The quantity $W$ is defined as

$$
W \equiv w\gamma^{2}\ ,
$$

where

$$
\begin{align}
w &= \rho_{b} + u + p\ ,\\
\gamma^{2} &= 1 + g_{ij}\tilde{u}^{i}\tilde{u}^{j}\ .
\end{align}
$$

Thus the quantities $g_{ij}\tilde{u}^{i}\tilde{u}^{j}$ and then $\gamma^{2}$ and $\gamma$. Thus, by computing $\rho_{b}$ and $p$ from the input variables, i.e. $D$, one can determine $w$ and then compute the value of $W$ from the input values (previous iteration), which we denote by $W_{\rm last}$.

**Dependecy note:** Note that this function depends on the `pressure_rho0_u()` function, which is *not* EOS independent.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* calculate W from last timestep and use for guess */
  utsq = 0. ;
  for(i=1;i<4;i++)
    for(j=1;j<4;j++) utsq += gcov[i][j]*prim[UTCON1+i-1]*prim[UTCON1+j-1] ;

  if( (utsq < 0.) && (fabs(utsq) < 1.0e-13) ) {
    utsq = fabs(utsq);
  }
  if(utsq < 0. || utsq > UTSQ_TOO_BIG) {
    retval = 2;
    return(retval) ;
  }

  gammasq = 1. + utsq ;
  gamma  = sqrt(gammasq);

  // Always calculate rho from D and gamma so that using D in EOS remains consistent
  //   i.e. you don't get positive values for dP/d(vsq) .
  rho0 = D / gamma ;
  u = prim[UU] ;
  p = pressure_rho0_u(eos, rho0,u) ;
  w = rho0 + u + p ;

  W_last = w*gammasq ;

  // Make sure that W is large enough so that v^2 < 1 :
  i_increase = 0;
  while( (( W_last*W_last*W_last * ( W_last + 2.*Bsq )
            - QdotBsq*(2.*W_last + Bsq) ) <= W_last*W_last*(Qtsq-Bsq*Bsq))
         && (i_increase < 10) ) {
    W_last *= 10.;
    i_increase++;
  }
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.b.iii: Compute $v^{2}_{\rm last}$, then update $v^{2}$ and $W$ \[Back to [top](#toc)\]
$$\label{utoprim_new_body__vsqlast_and_recompute_w_and_vsq}$$

Then we use equation (28) in [Noble *et al.* (2006)](https://arxiv.org/abs/astro-ph/0512420) to determine $v^{2}$:

$$
\boxed{v^{2} = \frac{\tilde{Q}^{2}W^{2} + \left(Q\cdot B\right)^{2}\left(B^{2}+2W\right)}{\left(B^{2}+W\right)^{2}W^{2}}}\ .
$$

This is done by calling the `x1_of_x0()` function, where $x_{0} = W$ and $x_{1} = v^{2}$, which itself calls the `vsq_calc()` function which implements the boxed equation above.

After we have $\left\{W_{\rm last},v^{2}_{\rm last}\right\}$ we use them as the initial guess for the `general_newton_raphson()`, which returns the updated values $\left\{W,v^{2}\right\}$.

All functions mentioned above are documented in this tutorial notebook, so look at the [Table of Contents](#toc) for more information.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  // Calculate W and vsq:
  x_2d[0] =  fabs( W_last );
  x_2d[1] = x1_of_x0( W_last , Bsq,QdotBsq,Qtsq,Qdotn,D) ;
  retval = general_newton_raphson( eos, x_2d, n, n_iter, func_vsq, Bsq,QdotBsq,Qtsq,Qdotn,D) ;

  W = x_2d[0];
  vsq = x_2d[1];

  /* Problem with solver, so return denoting error before doing anything further */
  if( (retval != 0) || (W == FAIL_VAL) ) {
    retval = retval*100+1;
    return(retval);
  }
  else{
    if(W <= 0. || W > W_TOO_BIG) {
      retval = 3;
      return(retval) ;
    }
  }

  // Calculate v^2:
  if( vsq >= 1. ) {
    vsq = 1.-2.e-16;
    //retval = 4;
    //return(retval) ;
  }
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.b.iv: Computing the primitive variables \[Back to [top](#toc)\]
$$\label{utoprim_new_body__compute_prims}$$

Now that we have $\left\{W,v^{2}\right\}$, we recompute the primitive variables. We start with

$$
\left\{
\begin{align}
\tilde{g} &\equiv \sqrt{1-v^{2}}\\
\gamma &= \frac{1}{\tilde{g}}
\end{align}
\right.
\implies
\boxed{\rho_{b} = D\tilde{g}}\ .
$$

Then, we determine the pressure $p$ using the `pressure_rho0_w()` function and

$$
w = W\left(1-v^{2}\right)
\implies
\boxed{u = w - \left(\rho_{b} + p\right)}\ .
$$

**Dependecy note:** Note that this function depends on the `pressure_rho0_w()` function, which is *not* EOS independent.

Finally, we can obtain $\tilde{u}^{i}$ using eq. 31 in [Noble *et al.* (2006)](https://arxiv.org/abs/astro-ph/0512420)

$$
\boxed{
\tilde{u}^{i} = \frac{\gamma}{\left(W+B^{2}\right)}\left[\tilde{Q}^{i} + \frac{\left(Q\cdot B\right)}{W}B^{i}\right]
}\ ,
$$

where

$$
\tilde{Q}^{i} = Q^{i} + \left(Q\cdot n\right)n^{i}\ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  // Recover the primitive variables from the scalars and conserved variables:
  gtmp = sqrt(1. - vsq);
  gamma = 1./gtmp ;
  rho0 = D * gtmp;

  w = W * (1. - vsq) ;
  p = pressure_rho0_w(eos, rho0,w) ;
  u = w - (rho0 + p) ; // u = rho0 eps, w = rho0 h

  if( (rho0 <= 0.) || (u <= 0.) ) {
    // User may want to handle this case differently, e.g. do NOT return upon
    // a negative rho/u, calculate v^i so that rho/u can be floored by other routine:

    retval = 5;
    //return(retval) ;
  }

  /*
    if(retval==5 && fabs(u)<1e-16) {
    u = fabs(u);
    CCTK_VInfo(CCTK_THORNSTRING,"%e\t%e\t%e",1.0-w/(rho0 + p),rho0,p);
    retval=0;
    }
  */

  prim[RHO] = rho0 ;
  prim[UU] = u ;

  for(i=1;i<4;i++)  Qtcon[i] = Qcon[i] + ncon[i] * Qdotn;
  for(i=1;i<4;i++) prim[UTCON1+i-1] = gamma/(W+Bsq) * ( Qtcon[i] + QdotB*Bcon[i]/W ) ;

  /* set field components */
  for(i = BCON1; i <= BCON3; i++) prim[i] = U[i] ;

  /* done! */
  return(retval) ;

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.c: The `vsq_calc()` function \[Back to [top](#toc)\]
$$\label{vsq_calc}$$

This function implements eq. (28) in [Noble *et al.* (2006)](https://arxiv.org/abs/astro-ph/0512420) to determine $v^{2}$:

$$
\boxed{v^{2} = \frac{\tilde{Q}^{2}W^{2} + \left(Q\cdot B\right)^{2}\left(B^{2}+2W\right)}{\left(B^{2}+W\right)^{2}W^{2}}}\ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/****************************************************************************
   vsq_calc():

      -- evaluate v^2 (spatial, normalized velocity) from
            W = \gamma^2 w

****************************************************************************/
static CCTK_REAL vsq_calc(CCTK_REAL W,CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D)
{
  CCTK_REAL Wsq,Xsq;

  Wsq = W*W ;
  Xsq = (Bsq + W) * (Bsq + W);

  return(  ( Wsq * Qtsq  + QdotBsq * (Bsq + 2.*W)) / (Wsq*Xsq) );
}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.d: The `x1_of_x0()` function \[Back to [top](#toc)\]
$$\label{x1_of_x0}$$

This function computes $v^{2}$, as described [above](#vsq_calc), then performs physical checks on $v^{2}$ (i.e. whether or not it is superluminal). This function assumes $W$ is physical.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/********************************************************************

  x1_of_x0():

    -- calculates v^2 from W  with some physical bounds checking;
    -- asumes x0 is already physical
    -- makes v^2 physical  if not;

*********************************************************************/

static CCTK_REAL x1_of_x0(CCTK_REAL x0, CCTK_REAL &Bsq, CCTK_REAL &QdotBsq, CCTK_REAL &Qtsq, CCTK_REAL &Qdotn, CCTK_REAL &D )
{
  CCTK_REAL vsq;
  CCTK_REAL dv = 1.e-15;

  vsq = fabs(vsq_calc(x0,Bsq,QdotBsq,Qtsq,Qdotn,D)) ; // guaranteed to be positive

  return( ( vsq > 1. ) ? (1.0 - dv) : vsq   );

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.e: The `validate_x()` function \[Back to [top](#toc)\]
$$\label{validate_x}$$

This function performs physical tests on $\left\{W,v^{2}\right\}$ based on their definitions.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/********************************************************************

  validate_x():

    -- makes sure that x[0,1] have physical values, based upon
       their definitions:

*********************************************************************/

static void validate_x(CCTK_REAL x[2], CCTK_REAL x0[2] )
{

  CCTK_REAL dv = 1.e-15;

  /* Always take the absolute value of x[0] and check to see if it's too big:  */
  x[0] = fabs(x[0]);
  x[0] = (x[0] > W_TOO_BIG) ?  x0[0] : x[0];

  x[1] = (x[1] < 0.) ?   0.       : x[1];  /* if it's too small */
  x[1] = (x[1] > 1.) ?  (1. - dv) : x[1];  /* if it's too big   */

  return;

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.f: The `general_newton_raphson()` function \[Back to [top](#toc)\]
$$\label{general_newton_raphson}$$

This function implements a [multidimensional Newton-Raphson method](https://en.wikipedia.org/wiki/Newton%27s_method#k_variables,_k_functions). We will not make the effort of explaining the algorithm exhaustively since it is pretty standard, so we will settle for a summary of the method.

Given a system of $N$ non-linear of equations and $N$ variables, $\left\{\vec{F}\!\left(\vec{x}\right),\vec{x}\right\}$, the Newton-Raphson method attempts to determine the root vector, $\vec{x}_{\star}$, iteratively through

$$
\begin{align}
\vec{x}_{n+1} = \vec{x}_{n} - J^{-1}_{F}\!\left(\vec{x}_{n}\right)\vec{F}\!\left(\vec{x}\right)\ ,
\end{align}
$$

where $J^{-1}_{F}$ is the Jacobian matrix

$$
\left(J_{F}\right)^{i}_{\ \ j} = \frac{\partial F^{i}}{\partial x^{j}}\ .
$$

The index $n$ above is an *iteration* index and  $\vec{x}_{n+1}$ represents an improved approximation to $\vec{x}_{\star}$ when compared to $\vec{x}_{n}$.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/************************************************************

  general_newton_raphson():

    -- performs Newton-Rapshon method on an arbitrary system.

    -- inspired in part by Num. Rec.'s routine newt();

*****************************************************************/
static int general_newton_raphson( eos_struct eos, CCTK_REAL x[], int n, long &n_iter,
                                   void (*funcd) (eos_struct, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [],
                                                  CCTK_REAL [][NEWT_DIM], CCTK_REAL *,
                                                  CCTK_REAL *, int,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &),CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D)
{
  CCTK_REAL f, df, dx[NEWT_DIM], x_old[NEWT_DIM];
  CCTK_REAL resid[NEWT_DIM], jac[NEWT_DIM][NEWT_DIM];
  CCTK_REAL errx, x_orig[NEWT_DIM];
  int    id, i_extra, doing_extra;

  int   keep_iterating;

  // Initialize various parameters and variables:
  errx = 1. ;
  df = f = 1.;
  i_extra = doing_extra = 0;
  for( id = 0; id < n ; id++)  x_old[id] = x_orig[id] = x[id] ;

  n_iter = 0;

  /* Start the Newton-Raphson iterations : */
  keep_iterating = 1;
  while( keep_iterating ) {

    (*funcd) (eos, x, dx, resid, jac, &f, &df, n, Bsq,QdotBsq,Qtsq,Qdotn,D);  /* returns with new dx, f, df */

    /* Save old values before calculating the new: */
    errx = 0.;
    for( id = 0; id < n ; id++) {
      x_old[id] = x[id] ;
    }

    /* Make the newton step: */
    for( id = 0; id < n ; id++) {
      x[id] += dx[id]  ;
    }

    /****************************************/
    /* Calculate the convergence criterion */
    /****************************************/
    errx  = (x[0]==0.) ?  fabs(dx[0]) : fabs(dx[0]/x[0]);

    /****************************************/
    /* Make sure that the new x[] is physical : */
    /****************************************/
    validate_x( x, x_old ) ;

    /*****************************************************************************/
    /* If we've reached the tolerance level, then just do a few extra iterations */
    /*  before stopping                                                          */
    /*****************************************************************************/

    if( (fabs(errx) <= NEWT_TOL) && (doing_extra == 0) && (EXTRA_NEWT_ITER > 0) ) {
      doing_extra = 1;
    }

    if( doing_extra == 1 ) i_extra++ ;

    if( ((fabs(errx) <= NEWT_TOL)&&(doing_extra == 0))
        || (i_extra > EXTRA_NEWT_ITER) || (n_iter >= (MAX_NEWT_ITER-1)) ) {
      keep_iterating = 0;
    }

    n_iter++;

  }   // END of while(keep_iterating)

  /*  Check for bad untrapped divergences : */
  if( (finite(f)==0) ||  (finite(df)==0) ) {
    return(2);
  }

  if( fabs(errx) > MIN_NEWT_TOL){
    //CCTK_VInfo(CCTK_THORNSTRING,"%d %e %e %e %e",n_iter,f,df,errx,MIN_NEWT_TOL);
    return(1);
  }
  if( (fabs(errx) <= MIN_NEWT_TOL) && (fabs(errx) > NEWT_TOL) ){
    return(0);
  }
  if( fabs(errx) <= NEWT_TOL ){
    return(0);
  }

  return(0);

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 2.g: The `func_vsq()` function \[Back to [top](#toc)\]
$$\label{func_vsq}$$

This function is used by the `general_newton_raphson()` function to compute the residuals and stepping. We will again not describe it in great detail since the method itself is relatively straightforward.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/*********************************************************************************
   func_vsq():

        -- calculates the residuals, and Newton step for general_newton_raphson();
        -- for this method, x=W,vsq here;

     Arguments:
          x   = current value of independent var's (on input & output);
         dx   = Newton-Raphson step (on output);
        resid = residuals based on x (on output);
         jac  = Jacobian matrix based on x (on output);
         f    =  resid.resid/2  (on output)
        df    = -2*f;  (on output)
         n    = dimension of x[];
*********************************************************************************/

static void func_vsq(eos_struct eos, CCTK_REAL x[], CCTK_REAL dx[], CCTK_REAL resid[],
                     CCTK_REAL jac[][NEWT_DIM], CCTK_REAL *f, CCTK_REAL *df, int n,
                     CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D)
{

  CCTK_REAL  W, vsq, Wsq, p_tmp, dPdvsq, dPdW;
  CCTK_REAL t11;
  CCTK_REAL t16;
  CCTK_REAL t18;
  CCTK_REAL t2;
  CCTK_REAL t21;
  CCTK_REAL t23;
  CCTK_REAL t24;
  CCTK_REAL t25;
  CCTK_REAL t3;
  CCTK_REAL t35;
  CCTK_REAL t36;
  CCTK_REAL t4;
  CCTK_REAL t40;
  CCTK_REAL t9;

  // vv TESTING vv
  //  CCTK_REAL D,gtmp,gamma,rho0,w,p,u;
  // ^^ TESTING ^^

  W = x[0];
  vsq = x[1];

  Wsq = W*W;

  // vv TESTING vv
  /*
    D = U[RHO] ;
    gtmp = sqrt(1. - vsq);
    gamma = 1./gtmp ;
    rho0 = D * gtmp;

    w = W * (1. - vsq) ;
    p = pressure_rho0_w(eos, rho0,w) ;
    u = w - (rho0 + p) ;

    if(u<=0 && 1==1) {
    vsq = 0.9999999 * (1.0-(rho0+p)/W);

    w = W * (1. - vsq) ;
    p = pressure_rho0_w(eos, rho0,w) ;
    u = w - (rho0 + p) ;

    //CCTK_VInfo(CCTK_THORNSTRING,"%e check",u);
    }
  */
  // ^^ TESTING ^^

  p_tmp  = pressure_W_vsq( eos, W, vsq , D);
  dPdW   = dpdW_calc_vsq( W, vsq );
  dPdvsq = dpdvsq_calc( eos, W, vsq, D );

  // These expressions were calculated using Mathematica, but made into efficient
  // code using Maple.  Since we know the analytic form of the equations, we can
  // explicitly calculate the Newton-Raphson step:

  t2 = -0.5*Bsq+dPdvsq;
  t3 = Bsq+W;
  t4 = t3*t3;
  t9 = 1/Wsq;
  t11 = Qtsq-vsq*t4+QdotBsq*(Bsq+2.0*W)*t9;
  t16 = QdotBsq*t9;
  t18 = -Qdotn-0.5*Bsq*(1.0+vsq)+0.5*t16-W+p_tmp;
  t21 = 1/t3;
  t23 = 1/W;
  t24 = t16*t23;
  t25 = -1.0+dPdW-t24;
  t35 = t25*t3+(Bsq-2.0*dPdvsq)*(QdotBsq+vsq*Wsq*W)*t9*t23;
  t36 = 1/t35;
  dx[0] = -(t2*t11+t4*t18)*t21*t36;
  t40 = (vsq+t24)*t3;
  dx[1] = -(-t25*t11-2.0*t40*t18)*t21*t36;
  //detJ = t3*t35; // <- set but not used...
  jac[0][0] = -2.0*t40;
  jac[0][1] = -t4;
  jac[1][0] = t25;
  jac[1][1] = t2;
  resid[0] = t11;
  resid[1] = t18;

  *df = -resid[0]*resid[0] - resid[1]*resid[1];

  *f = -0.5 * ( *df );

}
```

    Appending to ../src/harm_utoprim_2d.c

# Step 3: EOS dependent routines \[Back to [top](#toc)\]
$$\label{harm_utoprim_2d__c__eos_dep}$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************
 **********************************************************************

 The following routines specify the equation of state.  All routines
  above here should be indpendent of EOS.  If the user wishes
  to use another equation of state, the below functions must be replaced
  by equivalent routines based upon the new EOS.

  **********************************************************************
  **********************************************************************/
```

    Appending to ../src/harm_utoprim_2d.c

## Step 3.a: The `pressure_W_vsq()` function \[Back to [top](#toc)\]
$$\label{pressure_w_vsq}$$

This function computes $p\left(W,v^{2}\right)$. For a $\Gamma$-law equation of state,

$$
p_{\Gamma} = \left(\Gamma-1\right)u\ ,
$$

and with the definitions

$$
\begin{align}
\gamma^{2} &= \frac{1}{1-v^{2}}\ ,\\
W &= \gamma^{2}w\ ,\\
D &= \gamma\rho_{b}\ ,\\
w &= \rho_{b} + u + p\ ,
\end{align}
$$

we have

$$
\begin{align}
p_{\Gamma} &= \left(\Gamma-1\right)u\\
  &= \left(\Gamma-1\right)\left(w - \rho_{b} - p_{\Gamma}\right)\\
  &= \left(\Gamma-1\right)\left(\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\right) - \left(\Gamma-1\right)p_{\Gamma}\\
\implies
&\boxed{
p_{\Gamma} = \frac{\left(\Gamma-1\right)}{\Gamma}\left(\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\right)
}\ .
\end{align}
$$

Thus, the pre-PPEOS Patch version of this function was

```c
/**********************************************************************/
/**********************************************************************
  pressure_W_vsq():

        -- Gamma-law equation of state;
        -- pressure as a function of W, vsq, and D:
**********************************************************************/
static CCTK_REAL pressure_W_vsq(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
{

  CCTK_REAL gtmp;
  gtmp = 1. - vsq;

  return(  (GAMMA - 1.) * ( W * gtmp  -  D * sqrt(gtmp) ) / GAMMA  );

}
```

We are now, however, interested in the hybrid EOS of the form

$$
p_{\rm hybrid} = P_{\rm cold} + P_{\rm th}\ ,
$$

where $P_{\rm cold}$ is given by a single or piecewise polytrope EOS,

$$
P_{\rm cold} = K_{i}\rho_{b}^{\Gamma_{i}}\ ,
$$

$P_{\rm th}$ accounts for thermal effects and is given by

$$
P_{\rm th} = \left(\Gamma_{\rm th} - 1\right)\epsilon_{\rm th}\ ,
$$

and

$$
\begin{align}
\epsilon \equiv \frac{u}{\rho_{b}} &= \epsilon_{\rm th}+\epsilon_{\rm cold}\ ,\\
\epsilon_{\rm cold} &= \int d\rho \frac{P_{\rm cold}(\rho)}{\rho^{2}}\ .
\end{align}
$$

We then have

$$
\begin{align}
p_{\rm hybrid} &= P_{\rm cold} + P_{\rm th}\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\rho_{b}\epsilon_{\rm th}\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\rho_{b}\left(\epsilon - \epsilon_{\rm cold}\right)\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\left(u - \frac{D}{\gamma}\epsilon_{\rm cold}\right)\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\left(w - \rho_{b} - p_{\rm hybrid} - \frac{D}{\gamma}\epsilon_{\rm cold}\right)\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\left(\frac{W}{\gamma^{2}} - \frac{D}{\gamma} - \frac{D}{\gamma}\epsilon_{\rm cold}\right)-\left(\Gamma_{\rm th}-1\right)p_{\rm hybrid}\\
  &= P_{\rm cold} + \left(\Gamma_{\rm th}-1\right)\left[\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\left(1+\epsilon_{\rm cold}\right)\right]-\left(\Gamma_{\rm th}-1\right)p_{\rm hybrid}\\
\implies
&\boxed{ p_{\rm hybrid} = \frac{P_{\rm cold}}{\Gamma_{\rm th}} + \frac{\left(\Gamma_{\rm th}-1\right)}{\Gamma_{\rm th}}\left[\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\left(1+\epsilon_{\rm cold}\right)\right] }
\end{align}
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/**********************************************************************
  pressure_W_vsq():

        -- Hybrid single and piecewise polytropic equation of state;
        -- pressure as a function of P_cold, eps_cold, W, vsq, and D:
**********************************************************************/
static CCTK_REAL pressure_W_vsq(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
{

#ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
  DECLARE_CCTK_PARAMETERS;
#endif

  // Compute gamma^{-2} = 1 - v^{2} and gamma^{-1}
  CCTK_REAL inv_gammasq = 1.0 - vsq;
  CCTK_REAL inv_gamma   = sqrt(inv_gammasq);

  // Compute rho_b = D / gamma
  CCTK_REAL rho_b = D*inv_gamma;

  // Compute P_cold and eps_cold
  CCTK_REAL P_cold, eps_cold;
  compute_P_cold__eps_cold(eos,rho_b, P_cold,eps_cold);

  // Compute p = P_{cold} + P_{th}
  return( ( P_cold + (Gamma_th - 1.0)*( W*inv_gammasq - D*inv_gamma*( 1.0 + eps_cold ) ) )/Gamma_th );

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 3.b: The `dpdW_calc_vsq()` function \[Back to [top](#toc)\]
$$\label{dpdw_calc_vsq}$$

This function computes $\frac{\partial p\left(W,v^{2}\right)}{\partial W}$. For a $\Gamma$-law equation of state, remember that

$$
p_{\Gamma} = \frac{\left(\Gamma-1\right)}{\Gamma}\left(\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\right)\ ,
$$

which then implies

$$
\boxed{\frac{\partial p_{\Gamma}}{\partial W} = \frac{\Gamma-1}{\Gamma \gamma^{2}} = \frac{\left(\Gamma-1\right)\left(1-v^{2}\right)}{\Gamma}}\ .
$$

Thus, the pre-PPEOS Patch version of this function was

```c
/**********************************************************************/
/**********************************************************************
  dpdW_calc_vsq():

      -- partial derivative of pressure with respect to W;
**********************************************************************/
static CCTK_REAL dpdW_calc_vsq(CCTK_REAL W, CCTK_REAL vsq)
{

  return( (GAMMA - 1.) * (1. - vsq) /  GAMMA ) ;

}
```

For the case of a hybrid, single or piecewise polytropic EOS, we have

$$
p_{\rm hybrid} = \frac{P_{\rm cold}}{\Gamma_{\rm th}} + \frac{\left(\Gamma_{\rm th}-1\right)}{\Gamma_{\rm th}}\left[\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\left(1+\epsilon_{\rm cold}\right)\right]\ .
$$

It is important to notice that the cold components of $p_{\rm hybrid}$ are *not* functions of $W$, but instead functions of $D$: $P_{\rm cold} = P_{\rm cold}(\rho_{b}) = P_{\rm cold}(D)$ and $\epsilon_{\rm cold} = \epsilon_{\rm cold}(\rho_{b}) = \epsilon_{\rm cold}(D)$. Thus

$$
\boxed{\frac{\partial p_{\rm hybrid}}{\partial W} = \frac{\Gamma_{\rm th}-1}{\Gamma_{\rm th} \gamma^{2}} = \frac{\left(\Gamma_{\rm th}-1\right)\left(1-v^{2}\right)}{\Gamma_{\rm th}}}\ .
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/**********************************************************************
  dpdW_calc_vsq():

      -- partial derivative of pressure with respect to W;
**********************************************************************/
static CCTK_REAL dpdW_calc_vsq(CCTK_REAL W, CCTK_REAL vsq)
{

#ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
  DECLARE_CCTK_PARAMETERS;
#endif

  return( (Gamma_th - 1.0) * (1.0 - vsq) /  Gamma_th  ) ;

}
```

    Appending to ../src/harm_utoprim_2d.c

## Step 3.c: The `dpdvsq_calc()` function \[Back to [top](#toc)\]
$$\label{dpdvsq_calc}$$

This function computes $\frac{\partial p\left(W,v^{2}\right)}{\partial W}$. For a $\Gamma$-law equation of state, remember that

$$
p_{\Gamma} = \frac{\left(\Gamma-1\right)}{\Gamma}\left(\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\right) = \frac{\left(\Gamma-1\right)}{\Gamma}\left[W\left(1-v^{2}\right) - D\sqrt{1-v^{2}}\right]\ ,
$$

which then implies

$$
\boxed{\frac{\partial p_{\Gamma}}{\partial\left(v^{2}\right)} = \frac{\Gamma-1}{\Gamma}\left(\frac{D}{2\sqrt{1-v^{2}}}-W\right)} \ .
$$

Thus, the pre-PPEOS Patch version of this function was

```c
/**********************************************************************/
/**********************************************************************
  dpdvsq_calc():

      -- partial derivative of pressure with respect to vsq
**********************************************************************/
static CCTK_REAL dpdvsq_calc(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
{
  return( (GAMMA - 1.) * ( 0.5 * D / sqrt(1.-vsq)  - W  ) / GAMMA  ) ;
}
```

### Step 3.c.i: Setting basic quantities and computing $P_{\rm cold}$ and $\epsilon_{\rm cold}$ \[Back to [top](#toc)\]
$$\label{dpdvsq_calc__basic_quantities}$$

For the case of a hybrid, single or piecewise polytropic EOS, we have

$$
p_{\rm hybrid} = \frac{P_{\rm cold}}{\Gamma_{\rm th}} + \frac{\left(\Gamma_{\rm th}-1\right)}{\Gamma_{\rm th}}\left[\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\left(1+\epsilon_{\rm cold}\right)\right]\ .
$$

Let us thus begin by setting the necessary parameters from the hybrid EOS.

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

/**********************************************************************/
/**********************************************************************
  dpdvsq_calc():

      -- partial derivative of pressure with respect to vsq
**********************************************************************/
static CCTK_REAL dpdvsq_calc(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
{

  // This sets Gamma_th
#ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
  DECLARE_CCTK_PARAMETERS;
#endif

  // Set gamma and rho
  CCTK_REAL gamma = 1.0/sqrt(1.0 - vsq);
  CCTK_REAL rho_b = D/gamma;

  // Compute P_cold and eps_cold
  CCTK_REAL P_cold, eps_cold;
  compute_P_cold__eps_cold(eos,rho_b, P_cold,eps_cold);

  // Set basic polytropic quantities
  int polytropic_index = find_polytropic_K_and_Gamma_index(eos,rho_b);
  CCTK_REAL Gamma_ppoly_tab = eos.Gamma_ppoly_tab[polytropic_index];
```

    Appending to ../src/harm_utoprim_2d.c

### Step 3.c.ii: Computing $\frac{\partial P_{\rm cold}}{\partial\left(v^{2}\right)}$ \[Back to [top](#toc)\]
$$\label{dpdvsq_calc__dpcolddvsq}$$

Next, remember that $P_{\rm cold} = P_{\rm cold}(\rho_{b}) = P_{\rm cold}(D,v^{2})$ and also $\epsilon_{\rm cold} = \epsilon_{\rm cold}(D,v^{2})$. Therefore, we must start by finding the derivatives of $P_{\rm cold}$ and $\epsilon_{\rm cold}$ with respect to $v^{2}$.

Let us first notice that

$$
\frac{\partial\gamma}{\partial\left(v^{2}\right)} = \frac{\partial}{\partial\left(v^{2}\right)}\left[\frac{1}{\sqrt{1-v^{2}}}\right] = \frac{1}{2}\left(1-v^{2}\right)^{-3/2} = \frac{\gamma^{3}}{2}\ .
$$

Thus, for a general power

$$
\frac{\partial\gamma^{a}}{\partial\left(v^{2}\right)} = a\gamma^{a-1}\frac{\partial\gamma}{\partial\left(v^{2}\right)} = a\gamma^{a-1}\left(\frac{\gamma^{3}}{2}\right) = \frac{a}{2}\gamma^{a+2}
$$

Thus we have

$$
\begin{align}
\frac{\partial P_{\rm cold}}{\partial \left(v^{2}\right)}
&= \frac{\partial}{\partial\left(v^{2}\right)}\left(K_{\rm poly}\rho_{b}^{\Gamma_{\rm poly}}\right)\\
&= \frac{\partial}{\partial\left(v^{2}\right)}\left[K_{\rm poly}\left(\frac{D}{\gamma}\right)^{\Gamma_{\rm poly}}\right]\\
&= K_{\rm poly}D^{\Gamma_{\rm poly}}\frac{\partial}{\partial\left(v^{2}\right)}\left[\gamma^{-\Gamma_{\rm poly}/2}\right]\\
&=K_{\rm poly}D^{\Gamma_{\rm poly}}\left[\frac{-\Gamma_{\rm poly}/2}{2}\gamma^{-\Gamma_{\rm poly}/2 + 2}\right]\\
&=K_{\rm poly}\left(\frac{D}{\gamma}\right)^{\Gamma_{\rm poly}}\gamma^{-\frac{\Gamma_{\rm poly}}{2} + 2 + \Gamma_{\rm poly}}\\
\implies &\boxed{ \frac{\partial P_{\rm cold}}{\partial \left(v^{2}\right)} = \gamma^{2+\frac{\Gamma_{\rm poly}}{2}}P_{\rm cold}}\ .
\end{align}
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* Now we implement the derivative of P_cold with respect
   * to v^{2}, given by
   *  ----------------------------------------------------
   * | dP_cold/dvsq = gamma^{2 + Gamma_{poly}/2} P_{cold} |
   *  ----------------------------------------------------
   */
  CCTK_REAL dPcold_dvsq = P_cold * pow(gamma,2.0 + 0.5*Gamma_ppoly_tab);
```

    Appending to ../src/harm_utoprim_2d.c

### Step 3.c.iii: Computing $\frac{\partial \epsilon_{\rm cold}}{\partial\left(v^{2}\right)}$ \[Back to [top](#toc)\]
$$\label{dpdvsq_calc__depscolddvsq}$$

Now, obtaining $\epsilon_{\rm cold}$ from $P_{\rm cold}$ requires an integration and, therefore, generates an integration constant. Since we are interested in a *derivative* of $\epsilon_{\rm cold}$, however, we will simply drop the constant altogether. Remember that:

$$
\epsilon_{\rm cold} = K_{\rm poly}\int d\rho_{b} \rho_{b}^{\Gamma_{\rm poly}-2} = \frac{K_{\rm poly}\rho_{b}^{\Gamma_{\rm poly}-1}}{\Gamma_{\rm poly}-1} = \frac{P_{\rm cold}}{\rho_{b}\left(\Gamma_{\rm poly}-1\right)} = \frac{\gamma P_{\rm cold}}{D\left(\Gamma_{\rm poly}-1\right)}\ .
$$

Thus

$$
\begin{align}
\frac{\partial \epsilon_{\rm cold}}{\partial \left(v^{2}\right)}
&= \frac{1}{D\left(\Gamma_{\rm poly}-1\right)}\left[\gamma\frac{\partial P_{\rm cold}}{\partial \left(v^{2}\right)} + P_{\rm cold}\frac{\partial\gamma}{\partial \left(v^{2}\right)}\right]\\
&=\frac{1}{D\left(\Gamma_{\rm poly}-1\right)}\left[\gamma\frac{\partial P_{\rm cold}}{\partial \left(v^{2}\right)} + P_{\rm cold}\left(\frac{\gamma^{3}}{2}\right)\right]\\
\implies &\boxed{
\frac{\partial \epsilon_{\rm cold}}{\partial \left(v^{2}\right)} = \frac{\gamma}{D\left(\Gamma_{\rm poly}-1\right)}\left[\frac{\partial P_{\rm cold}}{\partial \left(v^{2}\right)} + \frac{\gamma^{2} P_{\rm cold}}{2}\right]\ .
}
\end{align}
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* Now we implement the derivative of eps_cold with respect
   * to v^{2}, given by
   *  -----------------------------------------------------------------------------------
   * | deps_cold/dvsq = gamma/(D*(Gamma_ppoly_tab-1)) * (dP_cold/dvsq + gamma^{2} P_cold / 2) |
   *  -----------------------------------------------------------------------------------
   */
  CCTK_REAL depscold_dvsq = ( gamma/(D*(Gamma_ppoly_tab-1.0)) ) * ( dPcold_dvsq + 0.5*gamma*gamma*P_cold );
```

    Appending to ../src/harm_utoprim_2d.c

### Step 3.c.iv: Computing $\frac{\partial p_{\rm hybrid}}{\partial\left(v^{2}\right)}$ \[Back to [top](#toc)\]
$$\label{dpdvsq_calc__dpdvsq}$$

Finally, remembering that

$$
\begin{align}
p_{\rm hybrid} &= \frac{P_{\rm cold}}{\Gamma_{\rm th}} + \frac{\left(\Gamma_{\rm th}-1\right)}{\Gamma_{\rm th}}\left[\frac{W}{\gamma^{2}} - \frac{D}{\gamma}\left(1+\epsilon_{\rm cold}\right)\right]\ ,\\
\frac{\partial\gamma^{a}}{\partial\left(v^{2}\right)} &= \frac{a}{2}\gamma^{a+2}\ ,
\end{align}
$$

we have

$$
\boxed{
\frac{\partial p_{\rm hybrid}}{\partial\left(v^{2}\right)}
= \frac{1}{\Gamma_{\rm th}}\left\{\frac{\partial P_{\rm cold}}{\partial\left(v^{2}\right)} + \left(\Gamma_{\rm th}-1\right)\left[-W + \frac{D\gamma}{2}\left(1+\epsilon_{\rm cold}\right) - \frac{D}{\gamma}\frac{\partial \epsilon_{\rm cold}}{\partial\left(v^{2}\right)}\right]\right\}\ .
}
$$

```python
%%writefile -a $outfile_path__harm_utoprim_2d__c

  /* Now we implement the derivative of p_hybrid with respect
   * to v^{2}, given by
   *  -----------------------------------------------------------------------------
   * | dp/dvsq = Gamma_th^{-1}( dP_cold/dvsq                                       |
   * |                          + (Gamma_{th}-1)*(-W                               |
   * |                                            + D gamma (1 + eps_cold)/2       |
   * |                                            - (D/gamma) * deps_cold/dvsq) )  |
   *  -----------------------------------------------------------------------------
   */
  return( ( dPcold_dvsq + (Gamma_th-1.0)*( -W + D*gamma*(1+eps_cold)/2.0 - D*depscold_dvsq/gamma ) )/Gamma_th );
}

/******************************************************************************
             END   OF   UTOPRIM_2D.C
******************************************************************************/
#endif

```

    Appending to ../src/harm_utoprim_2d.c

# Step 4: Code validation \[Back to [top](#toc)\]
$$\label{code_validation}$$

First we download the original `IllinoisGRMHD` source code and then compare it to the source code generated by this tutorial notebook.

```python
# Verify if the code generated by this tutorial module
# matches the original IllinoisGRMHD source code

# First download the original IllinoisGRMHD source code
import urllib
from os import path

original_IGM_file_url  = "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/IllinoisGRMHD/src/harm_utoprim_2d.c"
original_IGM_file_name = "harm_utoprim_2d-original.c"
original_IGM_file_path = os.path.join(IGM_src_dir_path,original_IGM_file_name)

# Then download the original IllinoisGRMHD source code
# We try it here in a couple of ways in an attempt to keep
# the code more portable
try:
    original_IGM_file_code = urllib.request.urlopen(original_IGM_file_url).read().decode("utf-8")
    # Write down the file the original IllinoisGRMHD source code
    with open(original_IGM_file_path,"w") as file:
        file.write(original_IGM_file_code)
except:
    try:
        original_IGM_file_code = urllib.urlopen(original_IGM_file_url).read().decode("utf-8")
        # Write down the file the original IllinoisGRMHD source code
        with open(original_IGM_file_path,"w") as file:
            file.write(original_IGM_file_code)
    except:
        # If all else fails, hope wget does the job
        !wget -O $original_IGM_file_path $original_IGM_file_url

# Perform validation
Validation__harm_utoprim_2d__c  = !diff $original_IGM_file_path $outfile_path__harm_utoprim_2d__c

if Validation__harm_utoprim_2d__c == []:
    # If the validation passes, we do not need to store the original IGM source code file
    !rm $original_IGM_file_path
    print("Validation test for harm_utoprim_2d.c: PASSED!")
else:
    # If the validation fails, we keep the original IGM source code file
    print("Validation test for harm_utoprim_2d.c: FAILED!")
    # We also print out the difference between the code generated
    # in this tutorial module and the original IGM source code
    print("Diff:")
    for diff_line in Validation__harm_utoprim_2d__c:
        print(diff_line)
```

    Validation test for harm_utoprim_2d.c: FAILED!
    Diff:
    0a1,2
    > #ifndef __HARM_UTOPRIM_2D__C__
    > #define __HARM_UTOPRIM_2D__C__
    70,72c72,74
    < static int Utoprim_new_body(CCTK_REAL U[], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM], CCTK_REAL gdet,  CCTK_REAL prim[],long &n_iter);
    < static int general_newton_raphson( CCTK_REAL x[], int n, long &n_iter, void (*funcd) (CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *, CCTK_REAL *, int,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &),CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
    < static void func_vsq( CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *f, CCTK_REAL *df, int n,CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
    ---
    > static int Utoprim_new_body(eos_struct eos, CCTK_REAL U[], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM], CCTK_REAL gdet,  CCTK_REAL prim[],long &n_iter);
    > static int general_newton_raphson( eos_struct eos, CCTK_REAL x[], int n, long &n_iter, void (*funcd) (eos_struct, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *, CCTK_REAL *, int,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &,CCTK_REAL &),CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
    > static void func_vsq( eos_struct eos, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [], CCTK_REAL [][NEWT_DIM], CCTK_REAL *f, CCTK_REAL *df, int n,CCTK_REAL &Bsq,CCTK_REAL &QdotBsq,CCTK_REAL &Qtsq,CCTK_REAL &Qdotn,CCTK_REAL &D);
    74c76
    < static CCTK_REAL pressure_W_vsq(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D) ;
    ---
    > static CCTK_REAL pressure_W_vsq(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D) ;
    76c78
    < static CCTK_REAL dpdvsq_calc(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D);
    ---
    > static CCTK_REAL dpdvsq_calc(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D);
    89,97c91,99
    <               /  rho u^t            \
    <          U =  |  T^t_t + rho u^t    |  sqrt(-det(g_{\mu\nu}))
    <               |  T^t_i              |
    <               \  B^i                /
    <
    <              /    rho        \
    <      P = |    uu         |
    <              | \tilde{u}^i   |
    <              \   B^i         /
    ---
    >              /     rho u^t     \
    >          U = | T^t_t + rho u^t |  sqrt(-det(g_{\mu\nu}))
    >              |      T^t_i      |
    >              \       B^i       /
    >
    >              /     rho     \
    >          P = |     uu      |
    >              | \tilde{u}^i |
    >              \     B^i     /
    101c103
    <        U[NPR]    = conserved variables (current values on input/output);
    ---
    >        U[NPR]           = conserved variables (current values on input/output);
    105,106c107,108
    <        prim[NPR] = primitive variables (guess on input, calculated values on
    <                                         output if there are no problems);
    ---
    >        prim[NPR]        = primitive variables (guess on input, calculated values on
    >                                                 output if there are no problems);
    114c116,117
    < int Utoprim_2d(CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM],
    ---
    >
    > int Utoprim_2d(eos_struct eos, CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM], CCTK_REAL gcon[NDIM][NDIM],
    130a134
    >
    141a146
    >
    150c155
    <   ret = Utoprim_new_body(U_tmp, gcov, gcon, gdet, prim_tmp,n_iter);
    ---
    >   ret = Utoprim_new_body(eos, U_tmp, gcov, gcon, gdet, prim_tmp,n_iter);
    163a169
    >
    175,177c181,183
    <              /  rho gamma        \
    <          U = |  alpha T^t_\mu  |
    <              \  alpha B^i        /
    ---
    >              /   rho gamma   \
    >          U = | alpha T^t_\mu |
    >              \   alpha B^i   /
    181,184c187,190
    <                /    rho        \
    <     prim = |    uu         |
    <                | \tilde{u}^i   |
    <                \  alpha B^i   /
    ---
    >              /     rho     \
    >       prim = |     uu      |
    >              | \tilde{u}^i |
    >              \  alpha B^i  /
    198c204
    <          3 -> failure: W<0 or W>W_TOO_BIG
    ---
    >              3 -> failure: W<0 or W>W_TOO_BIG
    204c210
    < static int Utoprim_new_body(CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM],
    ---
    > static int Utoprim_new_body(eos_struct eos, CCTK_REAL U[NPR], CCTK_REAL gcov[NDIM][NDIM],
    217a224
    >
    237a245,248
    >   // FIXME: The exact form of n^{\mu} can be found
    >   //        in eq. (2.116) and implementing it
    >   //        directly is a lot more efficient than
    >   //        performing n^{\mu} = g^{\mu\nu}n_{nu}
    248a260
    >
    270c282

   p = pressure_rho0_u(eos, rho0,u) ;
    283a296
    >
    288c301
    <   retval = general_newton_raphson( x_2d, n, n_iter, func_vsq, Bsq,QdotBsq,Qtsq,Qdotn,D) ;
    ---
    >   retval = general_newton_raphson( eos, x_2d, n, n_iter, func_vsq, Bsq,QdotBsq,Qtsq,Qdotn,D) ;
    311a325
    >
    318c332

   p = pressure_rho0_w(eos, rho0,w) ;
    352a367
    >
    371a387
    >
    393a410
    >
    419a437
    >
    429,430c447,448
    < static int general_newton_raphson( CCTK_REAL x[], int n, long &n_iter,
    <                                    void (*funcd) (CCTK_REAL [], CCTK_REAL [], CCTK_REAL [],
    ---
    > static int general_newton_raphson( eos_struct eos, CCTK_REAL x[], int n, long &n_iter,
    >                                    void (*funcd) (eos_struct, CCTK_REAL [], CCTK_REAL [], CCTK_REAL [],
    454c472
    <     (*funcd) (x, dx, resid, jac, &f, &df, n, Bsq,QdotBsq,Qtsq,Qdotn,D);  /* returns with new dx, f, df */
    ---
    >     (*funcd) (eos, x, dx, resid, jac, &f, &df, n, Bsq,QdotBsq,Qtsq,Qdotn,D);  /* returns with new dx, f, df */
    522a541
    >
    540c559
    < static void func_vsq(CCTK_REAL x[], CCTK_REAL dx[], CCTK_REAL resid[],
    ---
    > static void func_vsq(eos_struct eos, CCTK_REAL x[], CCTK_REAL dx[], CCTK_REAL resid[],
    579c598

     p = pressure_rho0_w(eos, rho0,w) ;
    586c605

     p = pressure_rho0_w(eos, rho0,w) ;
    595c614
    <   p_tmp  = pressure_W_vsq( W, vsq , D);
    ---
    >   p_tmp  = pressure_W_vsq( eos, W, vsq , D);
    597c616
    <   dPdvsq = dpdvsq_calc( W, vsq, D );
    ---
    >   dPdvsq = dpdvsq_calc( eos, W, vsq, D );
    635a655
    >
    646a667
    >
    651,652c672,673
    <         -- Gamma-law equation of state;
    <         -- pressure as a function of W, vsq, and D:
    ---
    >         -- Hybrid single and piecewise polytropic equation of state;
    >         -- pressure as a function of P_cold, eps_cold, W, vsq, and D:
    654c675
    < static CCTK_REAL pressure_W_vsq(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
    ---
    > static CCTK_REAL pressure_W_vsq(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
    655a677,678
    >
    > #ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
    656a680
    > #endif
    658,661c682,694
    <   CCTK_REAL gtmp;
    <   gtmp = 1. - vsq;
    <
    <   return(  (gamma_th /* <- Should be local polytropic Gamma factor */  - 1.) * ( W * gtmp  -  D * sqrt(gtmp) ) / gamma_th /* <- Should be local polytropic Gamma factor */   );
    ---
    >   // Compute gamma^{-2} = 1 - v^{2} and gamma^{-1}
    >   CCTK_REAL inv_gammasq = 1.0 - vsq;
    >   CCTK_REAL inv_gamma   = sqrt(inv_gammasq);
    >
    >   // Compute rho_b = D / gamma
    >   CCTK_REAL rho_b = D*inv_gamma;
    >
    >   // Compute P_cold and eps_cold
    >   CCTK_REAL P_cold, eps_cold;
    >   compute_P_cold__eps_cold(eos,rho_b, P_cold,eps_cold);
    >
    >   // Compute p = P_{cold} + P_{th}
    >   return( ( P_cold + (Gamma_th - 1.0)*( W*inv_gammasq - D*inv_gamma*( 1.0 + eps_cold ) ) )/Gamma_th );
    665a699
    >
    673a708,709
    >
    > #ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
    675c711,713
    <   return( (gamma_th /* <- Should be local polytropic Gamma factor */  - 1.) * (1. - vsq) /  gamma_th /* <- Should be local polytropic Gamma factor */  ) ;
    ---
    > #endif
    >
    >   return( (Gamma_th - 1.0) * (1.0 - vsq) /  Gamma_th  ) ;
    678a717
    >
    685c724
    < static CCTK_REAL dpdvsq_calc(CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
    ---
    > static CCTK_REAL dpdvsq_calc(eos_struct eos, CCTK_REAL W, CCTK_REAL vsq, CCTK_REAL &D)
    686a726,728
    >
    >   // This sets Gamma_th
    > #ifndef ENABLE_STANDALONE_IGM_C2P_SOLVER
    688c730,772
    <   return( (gamma_th /* <- Should be local polytropic Gamma factor */  - 1.) * ( 0.5 * D / sqrt(1.-vsq)  - W  ) / gamma_th /* <- Should be local polytropic Gamma factor */   ) ;
    ---
    > #endif
    >
    >
    >   // Set gamma and rho
    >   CCTK_REAL gamma = 1.0/sqrt(1.0 - vsq);
    >   CCTK_REAL rho_b = D/gamma;
    >
    >   // Compute P_cold and eps_cold
    >   CCTK_REAL P_cold, eps_cold;
    >   compute_P_cold__eps_cold(eos,rho_b, P_cold,eps_cold);
    >
    >   // Set basic polytropic quantities
    >   int polytropic_index = find_polytropic_K_and_Gamma_index(eos,rho_b);
    >   CCTK_REAL Gamma_ppoly_tab = eos.Gamma_ppoly_tab[polytropic_index];
    >
    >
    >   /* Now we implement the derivative of P_cold with respect
    >    * to v^{2}, given by
    >    *  ----------------------------------------------------
    >    * | dP_cold/dvsq = gamma^{2 + Gamma_{poly}/2} P_{cold} |
    >    *  ----------------------------------------------------
    >    */
    >   CCTK_REAL dPcold_dvsq = P_cold * pow(gamma,2.0 + 0.5*Gamma_ppoly_tab);
    >
    >
    >   /* Now we implement the derivative of eps_cold with respect
    >    * to v^{2}, given by
    >    *  -----------------------------------------------------------------------------------
    >    * | deps_cold/dvsq = gamma/(D*(Gamma_ppoly_tab-1)) * (dP_cold/dvsq + gamma^{2} P_cold / 2) |
    >    *  -----------------------------------------------------------------------------------
    >    */
    >   CCTK_REAL depscold_dvsq = ( gamma/(D*(Gamma_ppoly_tab-1.0)) ) * ( dPcold_dvsq + 0.5*gamma*gamma*P_cold );
    >
    >   /* Now we implement the derivative of p_hybrid with respect
    >    * to v^{2}, given by
    >    *  -----------------------------------------------------------------------------
    >    * | dp/dvsq = Gamma_th^{-1}( dP_cold/dvsq                                       |
    >    * |                          + (Gamma_{th}-1)*(-W                               |
    >    * |                                            + D gamma (1 + eps_cold)/2       |
    >    * |                                            - (D/gamma) * deps_cold/dvsq) )  |
    >    *  -----------------------------------------------------------------------------
    >    */
    >   return( ( dPcold_dvsq + (Gamma_th-1.0)*( -W + D*gamma*(1+eps_cold)/2.0 - D*depscold_dvsq/gamma ) )/Gamma_th );
    694a779
    > #endif

# Step 5: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-IllinoisGRMHD__harm_utoprim_2d.pdf](Tutorial-IllinoisGRMHD__harm_utoprim_2d.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means).

```python
latex_nrpy_style_path = os.path.join(nrpy_dir_path,"latex_nrpy_style.tplx")
#!jupyter nbconvert --to latex --template $latex_nrpy_style_path --log-level='WARN' Tutorial-IllinoisGRMHD__harm_utoprim_2d.ipynb
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__harm_utoprim_2d.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__harm_utoprim_2d.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__harm_utoprim_2d.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```


```python
%%HTML
<!-- Mejorar visualización en proyector -->
<style>
.rendered_html {font-size: 1.2em; line-height: 150%;}
div.prompt {min-width: 0ex; }
.container {width:95% !important;}
</style>
```

<!-- Mejorar visualización en proyector -->
<style>
.rendered_html {font-size: 1.2em; line-height: 150%;}
div.prompt {min-width: 0ex; }
.container {width:95% !important;}
</style>

```python
%matplotlib notebook
import numpy as np
import scipy.fftpack
import matplotlib.pyplot as plt
from matplotlib import animation, patches
from IPython.display import display, Audio, HTML
plt.rcParams['image.cmap'] = 'gray'
from ipywidgets import interact, FloatSlider, IntSlider, SelectionSlider, Layout
from functools import partial
slider_layout = Layout(width='600px', height='20px')
slider_style = {'description_width': 'initial'}
FloatSlider_nice = partial(FloatSlider, style=slider_style, layout=slider_layout, continuous_update=False)
IntSlider_nice = partial(IntSlider, style=slider_style, layout=slider_layout, continuous_update=False)
SelectionSlider_nice = partial(SelectionSlider, style=slider_style, layout=slider_layout, continuous_update=False)

def color2bw(img):
    return np.dot(img, [0.299, 0.587, 0.114])
```

***
### Universidad Austral de Chile
## Computación de alto rendimiento
# Fast Fourier Transform

### Cristóbal Navarro y Pablo Huijse

***

# Serie trigonométrica

Podemos descomponer una señal periódica $P=1/f$ en una suma de sinusoides

$$
\begin{align}
s(t)
&= \sum_{k=0}^\infty A_k \cos(2\pi k f t + \phi_k) \nonumber \\
&= \sum_{k=0}^\infty a_k \cos(2\pi k f t) + b_k \sin(2\pi k f t), \nonumber
\end{align}
$$

donde

$$
a_k =  f \int_{-\frac{1}{2f}}^{\frac{1}{2f}} s(t) \cos(2\pi k f t) \,dt \quad b_k = f \int_{-\frac{1}{2f}}^{\frac{1}{2f}} s(t) \sin(2\pi k f t) \,dt
$$

```python
plt.close('all'); fig, ax = plt.subplots(figsize=(6, 4))
f = 1.51518; t = np.linspace(-2/f, 2/f, num=5000);
line = ax.plot(t, np.zeros_like(t))
ax.set_xticks([-2/f, -1/f, 0, 1/f, 2/f]);
ax.set_xticklabels([r"$-2/f_0$", r"$-1/f_0$", "0", r"$1/f_0$", r"$2/f_0$"]);
a, b = np.random.randn(101), np.random.randn(101)
def update(K):
    y = np.zeros_like(t)
    for k in range(1, K+1):
        #y += (1/k)*np.sin(2.0*np.pi*k*f*t)
        #y += (2*np.sin(np.pi*k/2)/(np.pi*k))*np.cos(2.0*np.pi*k*f*t)
        y += a[k]*np.cos(2.0*np.pi*k*f*t)/k**2 + b[k]*np.sin(2.0*np.pi*k*f*t)/k**2
    line[0].set_ydata(y); ax.set_ylim([np.amin(y)*1.1, np.amax(y)*1.1])
interact(update, K=SelectionSlider_nice(options=[1, 2, 3, 4, 5, 10, 20, 30, 50, 100]));
```

    <IPython.core.display.Javascript object>

    interactive(children=(SelectionSlider(continuous_update=False, description='K', layout=Layout(height='20px', w…

Visualización interactiva de la FS: https://bl.ocks.org/jinroh/7524988

Serie de Fourier interesante: https://www.wolframalpha.com/input/?i=longcat+curve

# Transformada de Fourier
***
- El concepto de frecuencia puede aplicarse también a señales no-periódicas
- **Joseph Fourier:** Una señal no-periódica puede ser vista como una señal periódica **con un período infinito**
- El único requisito es que ahora las frecuencias son un continuo, con un espaciado infinitesimal

Directa:
$$
S(\omega) = \mathbb{FT}[s(t)] =  \int_{-\infty}^{\infty} s(t) e^{-j\omega t } dt,
$$

***

Inversa:
$$
s(t) =  \mathbb{FT}^{-1}[S(\omega)] = \frac{1}{2\pi} \int_{-\infty}^{\infty} S(\omega) e^{j \omega t } d\omega,
$$

***

# Discrete Fourier Transform (DFT)

***

- Nos interesa trabajar con señales digitales que están muestreadas en el tiempo
- Asumimos que la señal fue observada en un ventana de tiempo de ancho $T$ [s]
- Sea un sistema muestreador con frecuencia de muestreo $F_s$ [Hz] tal que
$$
s(t) = \sum_{n=0}^{N-1} s[n] \delta(t - n/F_s),
$$
del cual hemos tomado $N = T F_s$ muestras de $s(t)$

- Reemplazando en la transformada de Fourier
$$
\begin{align}
S(\omega) &= \int s(t) e^{-j\omega t} dt \nonumber \\
&= \int \sum_{n=0}^{N-1} s[n] \delta(t - n/F_s) e^{-j\omega t} dt \nonumber \\
&=  \sum_{n=0}^{N-1} s[n] \int \delta(t - n/F_s) e^{-j\omega t} dt \nonumber \\
&=  \sum_{n=0}^{N-1} s[n] e^{-j\omega n/F_s} \nonumber
\end{align}
$$
- Definiendo entonces $\omega = 2 \pi f = 2 \pi k \Delta f$ donde $\Delta f = \frac{1}{T} = \frac{F_s}{N}$ y reemplazando
$$
S[k] =  \sum_{n=0}^{N-1} s[n] e^{-j \frac{2 \pi}{N} k n},
$$
donde $k = [0, 1, \ldots N-1]$, ¿A qué frecuencias corresponden estos índices?

***

## DFT como producto matricial

Sea $\{s_n\}_{n=0,\ldots,N-1}$ y definiendo

$$
W_N = e^{-j \frac{2\pi}{N}} = \cos \left(\frac{2\pi}{N}\right) - j \sin \left(\frac{2\pi}{N}\right)
$$

podemos expresar la transformada de Fourier discreta como

$$
S[k] =  \sum_{n=0}^{N-1} s[n] W_N^{kn}, \quad k = [0, 1, \ldots N-1],
$$

que también puede ser expresado matricialmente como

$$
\begin{align}
\begin{pmatrix}
S[0] \\
S[1] \\
S[2] \\
\vdots \\
S[N-1] \\
\end{pmatrix} &=
\begin{pmatrix}
1 & 1 & 1 & \cdots & 1 \\
1 & W_N^1 & W_N^2 & \cdots & W_N^{N-1} \\
1 & W_N^2 & W_N^4 & \cdots & W_N^{N-2} \\
\vdots & \dots & \dots & \ddots &  \vdots \\
1 & W_N^{N-1} & W_N^{N-2} & \cdots & W_N^1\\
\end{pmatrix}
\begin{pmatrix}
s[0] \\
s[1] \\
s[2] \\
\vdots \\
s[N-1] \\
\end{pmatrix} \nonumber  \\
S &= \Omega s,
\end{align}
$$

Notemos que:
- Por definición $W_N^{kn} = \left(e^{-j \frac{2\pi}{N}}\right)^{kn} = e^{-j \frac{2\pi}{N}kn}$
- Por periodicidad $W_N^{2(N-1)} = W_N^{2(N-1) - N}  = W_N^{N-2}$
- También se tiene simetría hermítica: $W_N^{k(-n)} = W_N^{-kn} = (W_N^{kn})^*$
- $\Omega$ es una matriz cuadrada y simétrica

***
La DFT tiene complejidad cuadrática: $N^2$ multiplicaciones y $N$ sumas
***

```python
F_s = 100
t = np.linspace(0, 2, 2*F_s, endpoint=False)
x = np.sin(2*np.pi*t*5) + 0.5*np.cos(2*np.pi*t*12 + np.pi/4)

fig, ax = plt.subplots(figsize=(7, 3), tight_layout=True)
ax.plot(t, x)
ax.set_xlabel('Tiempo [s]'); ax.set_ylabel('Amplitud');

def DFT(x):
    N = len(x)
    W_N = np.exp(-1j*2*np.pi/N)
    n = np.arange(N)
    Omega = W_N**(n*n.reshape(1,-1).T)
    return np.dot(Omega, x)

fig, ax = plt.subplots(figsize=(7, 3), tight_layout=True)
ax.plot(np.linspace(0, F_s, num=len(t)), np.abs(DFT(x)))
ax.set_xlabel('Frecuencia [Hz]');
display(np.angle(DFT(x))[np.abs(DFT(x)) > 10]*180/np.pi);
```

***

# Transformada Rápida de Fourier (FFT)

***

- La computación de la DFT tiene complejidad $\mathcal{O}(N^2)$
- Existe una aproximación numérica con complejidad $\mathcal{O}(N\log N)$: la Fast Fourier Transform (FFT).

El algoritmo *radix-2* obtiene una FFT recursiva que explota las simetrías en la DFT

$$
\begin{align}
S[k] &=  \sum_{n=0}^{N-1} s[n] W_N^{kn} \nonumber \\
&= \sum_{n=0}^{N/2-1} s[2n] W_N^{k 2n} + \sum_{n=0}^{N/2-1} s[2n+1] W_N^{k(2n+1)} \nonumber \\
&= \sum_{n=0}^{N/2-1} s[2n] W_{N/2}^{kn} + W_N^{k} \sum_{n=0}^{N/2-1} s[2n+1] W_{N/2}^{kn} \nonumber \\
&= S_E[k] + W_N^{k} S_O[k] ~~ \forall k \in [0,N/2]  \nonumber
\end{align}
$$

Notar que se calculan dos "medias" DFT

Por periodicidad de la DFT tenemos que
$$
\begin{align}
S_E[k + N/2] &=  \sum_{n=0}^{N/2-1} s[2n] W_{N/2}^{(k+N/2)n} \nonumber \\
&=  \sum_{n=0}^{N/2-1} s[2n] W_{N/2}^{kn} \exp \left(-j2\pi n \right) = S_E[k], \nonumber
\end{align}
$$

e igualmente

$$
S_O[k + N/2] = S_O[k],
$$

juntando ambos tenemos que
$$
\begin{align}
S[k + N/2] &=  S_E[k + N/2] + W_{N}^{(k+N/2)} S_O[k + N/2] \nonumber  \\
&=  S_E[k] + W_{N}^{k} \exp \left(-j\pi\right) S_O[k] \nonumber \\
&=  S_E[k] - W_{N}^{k} S_O[k] \nonumber
\end{align}
$$

es decir

$$
\begin{align}
S[k] &=  S_E[k] + W_{N}^{k} S_O[k] \nonumber \\
S[k + N/2] &=  S_E[k] - W_{N}^{k} S_O[k] \quad \forall k \in [0,N/2]  \nonumber
\end{align}
$$

- La DFT de $k$ y $k+N/2$ difieren en un signo
- Se han explotado las simetrías de la DFT para reducir el costo computacional

Diagramas de mariposa: http://www.themobilestudio.net/the-fourier-transform-part-14

```python
t = np.linspace(0, 10, num=20)
x = np.random.randn(20)
plt.figure(figsize=(7, 4))
plt.plot(t, x, c='k', alpha=0.5)
plt.scatter(t[::2], x[::2], marker='x', zorder=100)
plt.scatter(t[1::2], x[1::2], marker='o', zorder=100);
```

```python
np.set_printoptions(precision=3)
display(scipy.fftpack.fft(x),
        DFT(x))
```

```python
S = DFT(x) # N*N multiplicaciones
Se = DFT(x[0::2]) # N/2*N/2
So = DFT(x[1::2]) # N/2*N/2
# Se y So = N*N/2 multiplicaciones
display(S[:10],
        Se + np.exp(-1j*2*np.pi*np.arange(10)/len(x))*So,
        S[10:],
        Se - np.exp(-1j*2*np.pi*np.arange(10)/len(x))*So)
```

# Trabajo práctico

Genere una señal y calcule su transformada de Fourier escribiendo una rutina en C++

Optimize su rutina usando el algoritmo FFT

Paralelice el algoritmo FFT usando OpenMP y CUDA

```python
times, dt = np.linspace(0, 10, num=2048, retstep=True)
freqs = 0.01 + 20*np.random.rand(5)
phases = np.pi*np.random.randn(5)
signal = np.zeros(shape=(len(times),))
for freq, phase in zip(freqs, phases):
    signal += np.cos(2.0*np.pi*times*freq + phase)

fig, ax = plt.subplots()
ax.plot(times, signal)
np.savetxt('signal.dat', signal)
```

    <IPython.core.display.Javascript object>

```python
fig, ax = plt.subplots(2, figsize=(7, 4))
SIGNAL = scipy.fftpack.fft(signal, n=len(times))
SIGNAL_plot = scipy.fftpack.fftshift(SIGNAL)
freqs_plot = scipy.fftpack.fftshift(scipy.fftpack.fftfreq(n=len(times), d=dt))
ax[0].plot(freqs_plot, np.abs(SIGNAL_plot))
ax[1].plot(freqs_plot, np.angle(SIGNAL_plot));
```

    <IPython.core.display.Javascript object>

```python
#%%bash
!make
%timeit -n1 !./prog 0
%timeit -n1 !./prog 1
#!./prog 1
!head spectrum.dat
!make clean
```

```python
SIGNAL_c = np.loadtxt("spectrum.dat")
SIGNAL_c_complex = SIGNAL_c[:, 0] + 1j*SIGNAL_c[:, 1]
np.allclose(SIGNAL, SIGNAL_c_complex, rtol=1e-9)
```

```python
fig, ax = plt.subplots(2, figsize=(7, 4), tight_layout=True)
ax[0].plot(freqs_plot, np.abs(SIGNAL_plot), alpha=0.5)
SIGNAL_c_plot = scipy.fftpack.fftshift(SIGNAL_c_complex)
ax[0].plot(freqs_plot, np.abs(SIGNAL_c_plot), alpha=0.5)
ax[1].plot(freqs_plot, np.abs(SIGNAL_plot) - np.abs(SIGNAL_c_plot));
```

# Apéndices

## Transformada de Fourier bidimensional

La DFT se puede aplicar a funciones multi-dimensionales. En el caso discreto de una señal bidimensional $g[n_1, n_2]$ con índices $n_1 \in [0, N_1-1]$ y $n_2 \in [0, N_2-1]$ tenemos

$$
G[k_1, k_2] = \sum_{n_1=0}^{N_1-1} \sum_{n_2=0}^{N_2-1} g[n_1, n_2] \exp \left ( -j2\pi  \left[\frac{n_1 k_1}{N_1} + \frac{n_2 k_2}{N_2} \right] \right)
$$
y su inversa

$$
g[n_1, n_2] = \frac{1}{N_1 N_2}\sum_{k_1=0}^{N_1-1} \sum_{k_2=0}^{N_2-1} G[k_1, k_2] \exp \left ( j2\pi  \left[\frac{n_1 k_1}{N_1} + \frac{n_2 k_2}{N_2} \right] \right)
$$

Notemos que

\begin{align}
G[k_1, k_2] &= \sum_{n_1=0}^{N_1-1} \left(\sum_{n_2=0}^{N_2-1} g[n_1, n_2] \exp \left (-j2\pi \frac{n_2 k_2}{N_2}\right) \right) \exp \left (-j2\pi \frac{n_1 k_1}{N_1}\right) \\
&= \sum_{n_1=0}^{N_1-1} \gamma_{n_1}[n_2] \exp \left (-j2\pi \frac{n_1 k_1}{N_1}\right),
\end{align}

*i.e.* se descompone como dos DFT de una dimensión. En cada paso podemos usar la FFT

```python
x = np.arange(0, 32, step=1)
X, Y = np.meshgrid(x, x)
fig, ax = plt.subplots(9, 9, figsize=(11, 11), tight_layout=False)
for n in range(9):
    for m in range(9):
        ax[n, m].matshow(np.cos(2.0*np.pi*X*m/len(x) + 2.0*np.pi*Y*n/len(x)),
                         cmap=plt.cm.RdBu_r, vmin=-1, vmax=1)
        ax[n, m].axis('off')
```

    <IPython.core.display.Javascript object>

```python
img_doge = color2bw(plt.imread('doge.jpg'))

plt.figure(figsize=(8, 5))
plt.imshow(img_doge)
plt.colorbar(orientation='horizontal');
```

```python
from scipy import fftpack
fig, ax = plt.subplots(1, 2, figsize=(10, 5), tight_layout=True)
S_img = fftpack.fft2(img_doge)
im = ax[0].imshow(fftpack.fftshift(np.log(1.+np.abs(S_img))))
fig.colorbar(im, ax=ax[0], orientation='horizontal')
im = ax[1].imshow(fftpack.fftshift(np.angle(S_img)))  # arctan(imag/real)
fig.colorbar(im, ax=ax[1], orientation='horizontal');
```

```python
plt.close('all'); fig, ax = plt.subplots(1, 2, figsize=(6, 3), tight_layout=True);
cy, cx = img_doge.shape[0]/2, img_doge.shape[1]/2
x = np.arange(0, img_doge.shape[1]); y = np.arange(0, img_doge.shape[0]);
X, Y = np.meshgrid(x, y)

def update(sigma1=1, sigma2=1):
    for ax_ in ax:
        ax_.cla()
        ax_.axis('off')
    mask1 = np.exp(-(((X-cx)/sigma1)**2 + ((Y-cy)/sigma1)**2))
    mask2 = np.exp(-(((X-cx)/sigma2)**2 + ((Y-cy)/sigma2)**2))
    im = ax[0].imshow(fftpack.fftshift(np.log(1.0+np.abs(S_img)))*(mask1-mask2))
    im = ax[1].imshow(np.real(fftpack.ifft2(fftpack.ifftshift(fftpack.fftshift(S_img)*(mask1-mask2)))))
interact(update, sigma1=FloatSlider_nice(min=1, max=200.0, value=200, description="$\sigma_1$"),
        sigma2=FloatSlider_nice(min=1, max=200.0, value=1, description="$\sigma_2$"));
```

```python

```


```python
# 그래프, 수학 기능 추가
# Add graph and math features
import pylab as py
import numpy as np
import numpy.linalg as nl
# 기호 연산 기능 추가
# Add symbolic operation capability
import sympy as sy

```

# 구글 페이지랭크 알고리듬
The PageRank Algorithm of Google

* 위키백과 기여자, '페이지랭크', 위키백과, , 2017년 9월 1일, 02:44 UTC, <https://ko.wikipedia.org/w/index.php?title=%ED%8E%98%EC%9D%B4%EC%A7%80%EB%9E%AD%ED%81%AC&oldid=19481213> [2018년 7월 31일에 접근]
* Wikipedia contributors, 'PageRank', Wikipedia, The Free Encyclopedia, 16 July 2018, 19:43 UTC, <https://en.wikipedia.org/w/index.php?title=PageRank&oldid=850584505> [accessed 31 July 2018]

다음 그림을 살펴 보자.

Let's take a look a the following figure.

위 연결 상태를 다음과 같은 행렬로 표시할 수 있다.
The connectivity status can be represented as a matrix as follows.

| m | A  | B  | C  | D  | E  | F  | G1 | G2 | G3 | G4 | G5 |
|:---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| A   | 0  | 0  | 0  | 1  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| B   | 0  | 0  | 1  | 1  | 1  | 1  | 1  |  1 |  1 |  0 |  0 |
| C   | 0  | 1  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| D   | 0  | 0  | 0  | 0  | 1  | 0  | 0  |  0 |  0 |  0 |  0 |
| E   | 0  | 0  | 0  | 0  | 0  | 1  | 1  |  1 |  1 |  1 |  1 |
| F   | 0  | 0  | 0  | 0  | 1  | 0  | 0  |  0 |  0 |  0 |  0 |
| G1  | 0  | 0  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| G2  | 0  | 0  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| G3  | 0  | 0  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| G4  | 0  | 0  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |
| G5  | 0  | 0  | 0  | 0  | 0  | 0  | 0  |  0 |  0 |  0 |  0 |

예를 들어 첫 행에서 A 절점으로 들어오는 연결선은 D 절점에서 오는 것 하나 뿐이다.

For example, in the first row, node A has only one incoming link from node D.

```python
m = np.array(
    [
        [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
        [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    ]
)

```

각 열의 합이 1이 되도록 만든다.
Make sum of each row to one.

```python
s = m.sum(axis=0)

```

```python
for j in range(m.shape[1]):
    if 1 < s[j]:
        si = 1.0 / s[j]
        for i in range(m.shape[0]):
            m[i, j] *= si

```

이제 행렬의 고유벡터를 구한다.
Now let's find the eigenvector of the matrix.

```python
eval, evac = nl.eig(m)

```

```python
eval

```

```python
py.matshow(evac)
py.colorbar()
py.axis('equal')

```

## Final Bell
마지막 종

```python
# stackoverfow.com/a/24634221
import os
os.system("printf '\a'");

```

```python

```


# Basics of Wick&d

## Loading the module

To use wick&d you will have to first import the module `wicked`. Here we abbreviate it with `w` for convenience. We also define a function (`latex`) to display objects in LaTeX format.

```python
import wicked as w
from IPython.display import display, Math, Latex

def latex(expr):
    """Function to render any object that has a member latex() function"""
    display(Math(expr.latex()))
```

## Defining orbital spaces

To use wick&d, you need to specify what type of reference state is used as a vacuum state. This information is stored in the `OrbitalsSpaceInfo` class, which holds information about the orbital space defined. We get access to this object (a singleton) via the function `osi()`. Calling the print function we can the see information about the orbital spaces defined. When wick&d is initialized, no orbital space is defined and no text is printed:

```python
osi = w.osi()
print(str(osi))
```

## Defining orbital spaces

To define an orbital space one must specify:
- The label (a single character, e.g., 'o' for occupied)
- The type of operator field (a string, currently the only option is `"fermion"`).
- The type of reference state associated with this space (a string, one of `["occupied", "unoccupied", "general"]`)
- The *pretty* indices that we associate with this space (e.g., `['i','j','k']` for occupied orbitals)

Wick&d defines three types of occupations associated to each space:
- **Occupied** (`occupied`): orbitals that are occupied in the vacuum (applies to fermions)
- **Unoccupied** (`unoccupied`): orbitals that are unoccupied in the vacuum
- **General** (`general`): orbitals that are partially occupied in the vacuum

## Examples of different reference states/orbital spaces

### Physical vacuum
The reference state is the physical vacuum state
\begin{equation}
| - \rangle
\end{equation}
In this case all orbitals are unoccupied. The following code initializes a single orbital space with label `p` and unoccupied orbitals:

```python
w.reset_space()
w.add_space("p", "fermion", "unoccupied", ['a','b','c','d','e','f'])
```

### Single determinant (Fermi vacuum)

The reference state is the determinant
\begin{equation}
| \Phi \rangle = |\psi_1 \cdots \psi_N \rangle
\end{equation}
In this case you need to specify only two spaces, occupied and unoccupied orbitals. The following code initializes two spaces: 1) occupied (`o`) and 2) virtual (`v`) orbitals,

```python
w.reset_space()
w.add_space("o", "fermion", "occupied", ['i','j','k','l','m'])
w.add_space("v", "fermion", "unoccupied", ['a','b','c','d','e','f'])
```

### Linear combination of determinants (Generalized vacuum)

This case requires three orbital spaces: core (`occupied`), active (`general`), and virtual (`unoccupied`).
The reference is a linear combination of determinants
\begin{equation}
| \Psi \rangle = \sum_\mu c_\mu | \Phi_\mu \rangle
\end{equation}
where each determinant $| \Phi_\mu \rangle $ is
\begin{equation}
| \Phi_\mu \rangle = \underbrace{\hat{a}^\dagger_u \hat{a}^\dagger_v \cdots}_{\text{active}} | \Phi_\mathrm{c} \rangle
\end{equation}
where the core determinant $\Phi_\mathrm{c} \rangle$ is defined as
\begin{equation}
| \Phi_\mathrm{c} \rangle = |\psi_1 \cdots \psi_{N_\mathrm{c}} \rangle.
\end{equation}
To specify this reference you can use the code:

```python
w.reset_space()
w.add_space("c", "fermion", "occupied", ['i','j','k','l','m'])
w.add_space("a", "fermion", "general", ['u','v','w','x','y','z'])
w.add_space("v", "fermion", "unoccupied", ['a','b','c','d','e','f'])
```

We can now verify that the orbital spaces have been updated

```python
print(str(osi))
```