# Start-to-Finish Example: Setting up Polytropic [TOV](https://en.wikipedia.org/wiki/Tolman%E2%80%93Oppenheimer%E2%80%93Volkoff_equation) Initial Data, in Curvilinear Coordinates

## Authors: Zach Etienne & Phil Chang
### Formatting improvements courtesy Brandon Clark

## This module sets up initial data for a TOV star in *spherical, isotropic coordinates*, using the *Numerical* ADM Spherical to BSSN Curvilinear initial data module (numerical = BSSN $\lambda^i$'s are computed using finite-difference derivatives instead of exact expressions).

**Module Status:**  Validated

**Validation Notes:** This module has been validated to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution (see [plots](#convergence) at bottom). Note that convergence at the surface of the star will be lower order due to the sharp drop to zero in $T^{\mu\nu}$.**

### NRPy+ Source Code for this module:
* [TOV/TOV_Solver.py](../edit/TOV/TOV_Solver.py) [\[**tutorial**\]](Tutorial-ADM_Initial_Data-TOV.ipynb): Tolman-Oppenheimer-Volkoff (TOV) initial data; defines all ADM variables and nonzero $T^{\mu\nu}$ components in Spherical basis.
* [BSSN/ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear.py](../edit/BSSN/ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear.py); [\[**tutorial**\]](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb): *Numerical* Spherical ADM$\to$Curvilinear BSSN converter function
* [BSSN/BSSN_constraints.py](../edit/BSSN/BSSN_constraints.py); [\[**tutorial**\]](Tutorial-BSSN_constraints.ipynb): Hamiltonian constraint in BSSN curvilinear basis/coordinates

## Introduction:
Here we use NRPy+ to generate initial data for a [simple polytrope TOV star](https://en.wikipedia.org/wiki/Tolman%E2%80%93Oppenheimer%E2%80%93Volkoff_equation).

The entire algorithm is outlined below, with NRPy+-based components highlighted in green.

1. Allocate memory for gridfunctions, including temporary storage for the RK4 time integration.
1. Set gridfunction values to initial data (**This module**).
1. Evolve the system forward in time using RK4 time integration. At each RK4 substep, do the following:
    1. Evaluate BSSN RHS expressions.
    1. Apply singular, curvilinear coordinate boundary conditions [*a la* the SENR/NRPy+ paper](https://arxiv.org/abs/1712.07658)
    1. Apply constraints on conformal 3-metric: $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$
1. At the end of each iteration in time, output the Hamiltonian constraint violation.
1. Repeat above steps at two numerical resolutions to confirm convergence to zero.

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

This module is organized as follows

1. [Step 1](#initializenrpy): Set core NRPy+ parameters for numerical grids and reference metric
1. [Step 2](#adm_id_tov): Set up ADM initial data for polytropic TOV Star
    1. [Step 2.a](#tov_interp): Interpolating the TOV data file as needed
    1. [Step 2.b](#source): Compute source terms $S_{ij}$, $S_{i}$, $S$, and $\rho$
    1. [Step 2.c](#jacobian): Jacobian transformation on the ADM/BSSN source terms
    1. [Step 2.d](#tensor): Rescale tensorial quantities
1. [Step 3](#adm_id_spacetime): Convert ADM spacetime quantity initial data from Spherical to BSSN Curvilinear coordinates
1. [Step 4](#validate): Validating that the TOV initial data satisfy the Hamiltonian constraint
    1. [Step 4.a](#ham_const_output): Output the Hamiltonian Constraint
    1. [Step 4.b](#apply_bcs): Apply singular, curvilinear coordinate boundary conditions
    1. [Step 4.c](#enforce3metric): Enforce conformal 3-metric $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$
1. [Step 5](#mainc): `TOV_Playground.c`: The Main C Code
1. [Step 6](#plot): Plotting the single-neutron-star initial data
1. [Step 7](#convergence): Validation: Convergence of numerical errors (Hamiltonian constraint violation) to zero
1. [Step 8](#latex_pdf_output): Output this module to $\LaTeX$-formatted PDF file

# Step 1: Set core NRPy+ parameters for numerical grids and reference metric \[Back to [top](#toc)\]
$$\label{initializenrpy}$$

```python
# First we import needed core NRPy+ modules
from outputC import *
import NRPy_param_funcs as par
import grid as gri
import loop as lp
import indexedexp as ixp
import finite_difference as fin
import reference_metric as rfm

thismodule = "TOV_ID_setup"

# Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)

# Then we set the coordinate system for the numerical grid
par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.

import BSSN.BSSN_quantities as Bq
par.set_parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf","phi")

#################
# Next output C headers related to the numerical grids we just set up:
#################

# First output the coordinate bounds xxmin[] and xxmax[]:
with open("BSSN/xxminmax.h", "w") as file:
    file.write("const REAL xxmin[3] = {"+str(rfm.xxmin[0])+","+str(rfm.xxmin[1])+","+str(rfm.xxmin[2])+"};\n")
    file.write("const REAL xxmax[3] = {"+str(rfm.xxmax[0])+","+str(rfm.xxmax[1])+","+str(rfm.xxmax[2])+"};\n")

# Generic coordinate NRPy+ file output, Part 2: output the conversion from (x0,x1,x2) to Cartesian (x,y,z)
outputC([rfm.xxCart[0],rfm.xxCart[1],rfm.xxCart[2]],["xCart[0]","xCart[1]","xCart[2]"],
        "BSSN/xxCart.h")

# Register the Hamiltonian as a gridfunction, to be used later.
H = gri.register_gridfunctions("AUX","H")
```

    Wrote to file "BSSN/xxCart.h"

# Step 2: Set up ADM initial data for polytropic TOV Star \[Back to [top](#toc)\]
$$\label{adm_id_tov}$$

As documented [in the TOV Initial Data NRPy+ Tutorial Module](Tutorial-TOV_Initial_Data.ipynb) ([older version here](Tutorial-GRMHD_UnitConversion.ipynb)), we will now set up TOV initial data, storing the densely-sampled result to file (***Courtesy Phil Chang***).

The TOV solver uses an ODE integration routine provided by scipy, so we first make sure that scipy is installed:

```python
!pip install scipy > /dev/null
```

    DEPRECATION: A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
    WARNING: You are using pip version 19.2.2, however version 19.2.3 is available.
    You should consider upgrading via the 'pip install --upgrade pip' command.

Next we call the [`TOV.TOV_Solver()` function](../edit/TOV/TOV_Solver.py) ([NRPy+ Tutorial module](Tutorial-ADM_Initial_Data-TOV.ipynb)) to set up the initial data, using the default parameters for initial data. This function outputs the solution to a file named "outputTOVpolytrope.txt".

```python
import TOV.TOV_Solver as TOV
TOV.TOV_Solver()
```

    Just generated a TOV star with R_Schw = 0.956568142523 , M = 0.14050303285288188 , M/R_Schw = 0.1468824086931645 .

## Step 2.a: Interpolating the TOV data file as needed \[Back to [top](#toc)\]
$$\label{tov_interp}$$

The TOV data file just written stored $\left(r,\rho(r),P(r),M(r),e^{\nu(r)}\right)$, where $\rho(r)$ is the total mass-energy density (cf. $\rho_{\text{baryonic}}$).

**METRIC DATA IN TERMS OF ADM QUANTITIES**

The [TOV line element](https://en.wikipedia.org/wiki/Tolman%E2%80%93Oppenheimer%E2%80%93Volkoff_equation) in *Schwarzschild coordinates* is written (in the $-+++$ form):
$$
ds^2 = - c^2 e^\nu dt^2 + \left(1 - \frac{2GM}{rc^2}\right)^{-1} dr^2 + r^2 d\Omega^2.
$$

In *isotropic coordinates* with $G=c=1$ (i.e., the coordinate system and units we'd prefer to use), the ($-+++$ form) line element is written:
$$
ds^2 = - e^{\nu} dt^2 + e^{4\phi} \left(d\bar{r}^2 + \bar{r}^2 d\Omega^2\right),
$$
where $\phi$ here is the *conformal factor*.

The ADM 3+1 line element for this diagonal metric in isotropic spherical coordinates is given by:
$$
ds^2 = (-\alpha^2 + \beta_k \beta^k) dt^2 + \gamma_{\bar{r}\bar{r}} d\bar{r}^2 + \gamma_{\theta\theta} d\theta^2+ \gamma_{\phi\phi} d\phi^2,
$$

from which we can immediately read off the ADM quantities:
\begin{align}
\alpha &= e^{\nu(\bar{r})/2} \\
\beta^k &= 0 \\
\gamma_{\bar{r}\bar{r}} &= e^{4\phi}\\
\gamma_{\theta\theta} &= e^{4\phi} \bar{r}^2 \\
\gamma_{\phi\phi} &= e^{4\phi} \bar{r}^2 \sin^2 \theta \\
\end{align}

**STRESS-ENERGY TENSOR $T^{\mu\nu}$**

We will also need the stress-energy tensor $T^{\mu\nu}$. [As discussed here](https://en.wikipedia.org/wiki/Tolman%E2%80%93Oppenheimer%E2%80%93Volkoff_equation), the stress-energy tensor is diagonal:

\begin{align}
T^t_t &= -\rho \\
T^i_j &= P \delta^i_j \\
\text{All other components of }T^\mu_\nu &= 0.
\end{align}

Since $\beta^i=0$ the inverse metric expression simplifies to (Eq. 4.49 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf)):
$$
g^{\mu\nu} = \begin{pmatrix}
-\frac{1}{\alpha^2} & \frac{\beta^i}{\alpha^2} \\
\frac{\beta^i}{\alpha^2} & \gamma^{ij} - \frac{\beta^i\beta^j}{\alpha^2}
\end{pmatrix} =
\begin{pmatrix}
-\frac{1}{\alpha^2} & 0 \\
0 & \gamma^{ij}
\end{pmatrix},
$$

and since the 3-metric is diagonal we get

\begin{align}
\gamma^{\bar{r}\bar{r}} &= e^{-4\phi}\\
\gamma^{\theta\theta} &= e^{-4\phi}\frac{1}{\bar{r}^2} \\
\gamma^{\phi\phi} &= e^{-4\phi}\frac{1}{\bar{r}^2 \sin^2 \theta}.
\end{align}

Thus raising $T^\mu_\nu$ yields a diagonal $T^{\mu\nu}$

\begin{align}
T^{tt} &= -g^{tt} \rho = \frac{1}{\alpha^2} \rho = e^{-\nu(\bar{r})} \rho \\
T^{\bar{r}\bar{r}} &= g^{\bar{r}\bar{r}} P = \frac{1}{e^{4 \phi}} P \\
T^{\theta\theta} &= g^{\theta\theta} P = \frac{1}{e^{4 \phi}\bar{r}^2} P\\
T^{\phi\phi} &= g^{\phi\phi} P = \frac{1}{e^{4\phi}\bar{r}^2 \sin^2 \theta} P
\end{align}

As all input quantities are functions of $r$, we will simply read the solution from file and interpolate it to the values of $r$ needed by the initial data.

1. First we define functions `ID_TOV_ADM_quantities()` and `ID_TOV_TUPMUNU()` that call the [1D TOV interpolator function](../edit/TOV/tov_interp.h) to evaluate the ADM spacetime quantities and $T^{\mu\nu}$, respectively, at any given point $(r,\theta,\phi)$ in the Spherical basis. All quantities are defined as above.
1. Next we will construct the BSSN/ADM source terms $\{S_{ij},S_{i},S,\rho\}$ in the Spherical basis
1. Then we will perform the Jacobian transformation on $\{S_{ij},S_{i},S,\rho\}$ to the desired (xx0,xx1,xx2) basis
1. Next we call the *Numerical* Spherical ADM$\to$Curvilinear BSSN converter function to conver the above ADM quantities to the rescaled BSSN quantities in the desired curvilinear coordinate system: [BSSN/ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear.py](../edit/BSSN/ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear.py); [\[**tutorial**\]](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).

```python
with open("BSSN/ID_TOV_ADM_quantities.h", "w") as file:
    file.write("""
// This function takes as input either (x,y,z) or (r,th,ph) and outputs
//   all ADM quantities in the Cartesian or Spherical basis, respectively.
void ID_TOV_ADM_quantities(
                     const REAL xyz_or_rthph[3],

                     const ID_inputs other_inputs,

                     REAL *gammaDD00,REAL *gammaDD01,REAL *gammaDD02,REAL *gammaDD11,REAL *gammaDD12,REAL *gammaDD22,
                     REAL *KDD00,REAL *KDD01,REAL *KDD02,REAL *KDD11,REAL *KDD12,REAL *KDD22,
                     REAL *alpha,
                     REAL *betaU0,REAL *betaU1,REAL *betaU2,
                     REAL *BU0,REAL *BU1,REAL *BU2) {
      const REAL r  = xyz_or_rthph[0];
      const REAL th = xyz_or_rthph[1];
      const REAL ph = xyz_or_rthph[2];

      REAL rho,P,M,expnu,exp4phi;
      TOV_interpolate_1D(r,other_inputs.Rbar,other_inputs.Rbar_idx,other_inputs.interp_stencil_size,
                         other_inputs.numlines_in_file,
                         other_inputs.r_Schw_arr,other_inputs.rho_arr,other_inputs.P_arr,other_inputs.M_arr,
                         other_inputs.expnu_arr,other_inputs.exp4phi_arr,other_inputs.rbar_arr,
                         &rho,&P,&M,&expnu,&exp4phi);

      *alpha = sqrt(expnu);
      // \gamma_{rbar rbar} = exp(4 phi)
      *gammaDD00 = exp4phi;
      // \gamma_{thth} = r^2 * exp(4 phi)
      *gammaDD11 = r*r * exp4phi;
      // \gamma_{phph} = r^2 sin^2(th) * exp(4 phi)
      *gammaDD22 = r*r*sin(th)*sin(th) * exp4phi;

      // All other quantities ARE ZERO:
      *gammaDD01 = 0.0;
      *gammaDD02 = 0.0;
      *gammaDD12 = 0.0;

      *KDD00 = 0.0;
      *KDD01 = 0.0;
      *KDD02 = 0.0;
      *KDD11 = 0.0;
      *KDD12 = 0.0;
      *KDD22 = 0.0;

      *betaU0 = 0.0;
      *betaU1 = 0.0;
      *betaU2 = 0.0;

      *BU0 = 0.0;
      *BU1 = 0.0;
      *BU2 = 0.0;
}\n""")

with open("BSSN/ID_TOV_TUPMUNU.h", "w") as file:
    file.write("""
// This function takes as input either (x,y,z) or (r,th,ph) and outputs
//   all ADM quantities in the Cartesian or Spherical basis, respectively.
void ID_TOV_TUPMUNU(const REAL xyz_or_rthph[3], const ID_inputs other_inputs,

                    REAL *T4UU00,REAL *T4UU01,REAL *T4UU02,REAL *T4UU03,
                    /**/         REAL *T4UU11,REAL *T4UU12,REAL *T4UU13,
                    /**/                      REAL *T4UU22,REAL *T4UU23,
                    /**/                                   REAL *T4UU33) {
      const REAL r  = xyz_or_rthph[0];
      const REAL th = xyz_or_rthph[1];
      const REAL ph = xyz_or_rthph[2];

      REAL rho,P,M,expnu,exp4phi;
      TOV_interpolate_1D(r,other_inputs.Rbar,other_inputs.Rbar_idx,other_inputs.interp_stencil_size,
                         other_inputs.numlines_in_file,
                         other_inputs.r_Schw_arr,other_inputs.rho_arr,other_inputs.P_arr,other_inputs.M_arr,
                         other_inputs.expnu_arr,other_inputs.exp4phi_arr,other_inputs.rbar_arr,
                         &rho,&P,&M,&expnu,&exp4phi);

      //T^tt = e^(-nu) * rho
      *T4UU00 = rho / expnu;
      //T^rr = P / exp4phi
      *T4UU11 = P / exp4phi;
      //T^thth = P / (r^2 * exp4phi)
      *T4UU22 = P / (r*r * exp4phi);
      //T^phph = P / (r^2 * sin^2(th) * exp4phi)
      *T4UU33 = P / (r*r * sin(th)*sin(th) * exp4phi);

      // All other components ARE ZERO:
      *T4UU01 = 0; *T4UU02 = 0; *T4UU03 = 0;
      /**/         *T4UU12 = 0; *T4UU13 = 0;
      /**/                      *T4UU23 = 0;
}\n""")
```

## Step 2.b: Compute source terms $S_{ij}$, $S_{i}$, $S$, and $\rho$ \[Back to [top](#toc)\]
$$\label{source}$$

Compute source terms $S_{ij}$, $S_{i}$, $S$, and $\rho$, via Eqs. 10 of [Baumgarte, Montero, Cordero-Carrión, and Müller](https://arxiv.org/pdf/1211.6632.pdf):

\begin{array}
\ S_{ij} &= \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} \\
S_{i} &= -\gamma_{i\mu} n_\nu T^{\mu\nu} \\
S &= \gamma^{ij} S_{ij} \\
\rho &= n_\mu n_\nu T^{\mu\nu},
\end{array}

`ID_TOV_TUPMUNU()` provides numerical values for $T^{\mu\nu}$, but we do not have $\gamma_{\mu \nu}$ or $n_\mu$ directly. So here we will construct the latter quantities.

First, B&S Eq. 2.27 defines $\gamma_{\mu \nu}$ as:

$$\gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu$$

where

$$
n_\mu = \{-\alpha,0,0,0\}.
$$

So we will first need to construct the 4-metric based on ADM quantities. This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf):
$$
g_{\mu\nu} = \begin{pmatrix}
-\alpha^2 + \beta^k \beta_k & \beta_i \\
\beta_j & \gamma_{ij}
\end{pmatrix}.
$$

```python
gammaDD = ixp.declarerank2("gammaDD", "sym01",DIM=3)
betaU   = ixp.declarerank1("betaU",DIM=3)
alpha   = sp.symbols("alpha")

# To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S:

# Eq. 2.121 in B&S
betaD = ixp.zerorank1()
for i in range(DIM):
    for j in range(DIM):
        betaD[i] += gammaDD[i][j]*betaU[j]

# Now compute the beta contraction.
beta2 = sp.sympify(0)
for i in range(DIM):
    beta2 += betaU[i]*betaD[i]

# Eq. 2.122 in B&S
g4DD = ixp.zerorank2(DIM=4)
g4DD[0][0] = -alpha**2 + beta2
for i in range(DIM):
    g4DD[i+1][0] = g4DD[0][i+1] = betaD[i]
for i in range(DIM):
    for j in range(DIM):
        g4DD[i+1][j+1] = gammaDD[i][j]
```

Now let's construct $\gamma_{\mu\nu}$=`gamma4DD[mu][nu]` via $\gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu$:

```python
n4D = ixp.zerorank1(DIM=4)
n4D[0] = -alpha

gamma4DD = ixp.zerorank2(DIM=4)
for mu in range(4):
    for nu in range(4):
        gamma4DD[mu][nu] = g4DD[mu][nu] + n4D[mu]*n4D[nu]
```

We now have all we need to construct the BSSN source terms in the current basis (for TOV, the Spherical basis):

\begin{array}
\ S_{ij} &= \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} \\
S_{i} &= -\gamma_{i\mu} n_\nu T^{\mu\nu} \\
S &= \gamma^{ij} S_{ij} \\
\rho &= n_\mu n_\nu T^{\mu\nu},
\end{array}

```python
T4UU = ixp.declarerank2("T4UU", "sym01",DIM=4)

SDD = ixp.zerorank2()
SD  = ixp.zerorank1()
S   = sp.sympify(0)
rho = sp.sympify(0)

for i in range(DIM):
    for j in range(DIM):
        for mu in range(4):
            for nu in range(4):
                SDD[i][j] += gamma4DD[i+1][mu]*gamma4DD[j+1][nu] * T4UU[mu][nu]

for i in range(DIM):
    for mu in range(4):
        for nu in range(4):
            SD[i] += -gamma4DD[i+1][mu]*n4D[nu] * T4UU[mu][nu]

gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
for i in range(DIM):
    for j in range(DIM):
        S += gammaUU[i][j]*SDD[i][j]

for mu in range(4):
    for nu in range(4):
        rho += n4D[mu]*n4D[nu] * T4UU[mu][nu]
```

## Step 2.c: Jacobian transformation on the ADM/BSSN source terms \[Back to [top](#toc)\]
$$\label{jacobian}$$

The following discussion holds for either Spherical or Cartesian input data, so for simplicity let's just assume the data are given in Spherical coordinates.

All ADM tensors and vectors are in the Spherical coordinate basis $x^i_{\rm Sph} = (r,\theta,\phi)$, but we need them in the curvilinear coordinate basis $x^i_{\rm rfm}=$`(xx0,xx1,xx2)` set by the `"reference_metric::CoordSystem"` variable. Empirically speaking, it is far easier to write `(x(xx0,xx1,xx2),y(xx0,xx1, xx2),z(xx0,xx1,xx2))` than the inverse, so we will compute the Jacobian matrix

$$
{\rm Jac\_dUSph\_dDrfmUD[i][j]} = \frac{\partial x^i_{\rm Sph}}{\partial x^j_{\rm rfm}},
$$

via exact differentiation (courtesy SymPy), and the inverse Jacobian
$$
{\rm Jac\_dUrfm\_dDSphUD[i][j]} = \frac{\partial x^i_{\rm rfm}}{\partial x^j_{\rm Sph}},
$$

using NRPy+'s `generic_matrix_inverter3x3()` function. In terms of these, the transformation of BSSN tensors from Spherical to `"reference_metric::CoordSystem"` coordinates may be written:

\begin{align}
S^{\rm rfm}_{ij} &=
\frac{\partial x^\ell_{\rm Sph}}{\partial x^i_{\rm rfm}}
\frac{\partial x^m_{\rm Sph}}{\partial x^j_{\rm rfm}} S^{\rm Sph}_{\ell m}\\
S^{\rm rfm}_{i} &=
\frac{\partial x^\ell_{\rm Sph}}{\partial x^i_{\rm rfm}}
S^{\rm Sph}_{\ell}
\end{align}

```python
# UIUC Black Hole initial data are given in Spherical coordinates.
CoordType_in = "Spherical"

SSphorCartDD = ixp.zerorank2()
SSphorCartD  = ixp.zerorank1()

# Copy what we had written above, which was in Spherical coordinates, to the new tensors SSphorCartDD / SSphorCartD:
for i in range(3):
    SSphorCartD[i] = SD[i]
    for j in range(3):
        SSphorCartDD[i][j] = SDD[i][j]

# Zero out the original tensors; we're going to store the result to SD and SDD:
SDD = ixp.zerorank2()
SD  = ixp.zerorank1()

# Make sure that rfm.reference_metric() has been called.
#    We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
    print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
    print("       first setting up reference metric, by calling rfm.reference_metric().")
    exit(1)

r_th_ph_or_Cart_xyz_oID_xx = []
if CoordType_in == "Spherical":
    r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
    r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
else:
    print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
    exit(1)

# Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis

# rho and S are scalar, so no Jacobian transformations are necessary.

Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
    for j in range(DIM):
        Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i],rfm.xx[j])

Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)

for i in range(DIM):
    for j in range(DIM):
        SD[i] += Jac_dUSphorCart_dDrfmUD[j][i] * SSphorCartD[j]
        for k in range(DIM):
            for l in range(DIM):
                SDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i]*Jac_dUSphorCart_dDrfmUD[l][j] * SSphorCartDD[k][l]
```

## Step 2.d: Rescale tensorial quantities \[Back to [top](#toc)\]
$$\label{tensor}$$

We rescale tensorial quantities according to the prescription described in the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):

Since `ReD[i]}=1/ReU[i]`, we have

\begin{align}
s_{ij} &= S_{ij} /\text{ReDD[i][j]}\\
s_{i} &= S_i \text{ReU[i]}
\end{align}

```python
# Finally rescale the tensorial quantities:
sD = ixp.zerorank1()
sDD = ixp.zerorank2()
for i in range(DIM):
    sD[i] = SD[i] * rfm.ReU[i] # ReD[i] = 1/ReU[i]
    for j in range(DIM):
        sDD[i][j] = SDD[i][j] / rfm.ReDD[i][j]
```

Next we use NRPy+ to write a C function that reads in $T^{\mu\nu}$ in the given (Spherical) basis and outputs the source terms $\{S_{ij},S_{i},S,\rho\}$ in the `(xx0,xx1,xx2)` basis:

```python
with open("BSSN/ID_TOV_BSSN_Source_Terms.h", "w") as file:
    file.write("""void ID_TOV_BSSN_Source_Terms(
                    REAL xx0xx1xx2[3],

                    const REAL gammaDD00,const REAL gammaDD01,const REAL gammaDD02,
                    /**/                 const REAL gammaDD11,const REAL gammaDD12,
                    /**/                                      const REAL gammaDD22,

                    const REAL betaU0,const REAL betaU1,const REAL betaU2,
                    const REAL alpha,

                    const REAL T4UU00,const REAL T4UU01,const REAL T4UU02,const REAL T4UU03,
                    /**/              const REAL T4UU11,const REAL T4UU12,const REAL T4UU13,
                    /**/                                const REAL T4UU22,const REAL T4UU23,
                    /**/                                                  const REAL T4UU33,

                    REAL *sDD00,REAL *sDD01,REAL *sDD02,
                    /**/        REAL *sDD11,REAL *sDD12,
                    /**/                    REAL *sDD22,

                    REAL *sD0, REAL *sD1, REAL *sD2,

                    REAL *S, REAL *rho) {

    const REAL xx0 = xx0xx1xx2[0];
    const REAL xx1 = xx0xx1xx2[1];
    const REAL xx2 = xx0xx1xx2[2];\n""")
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"

outputC([sDD[0][0],sDD[0][1],sDD[0][2],sDD[1][1],sDD[1][2],sDD[2][2],
         sD[0],sD[1],sD[2], S, rho],
        ["*sDD00","*sDD01","*sDD02","*sDD11","*sDD12","*sDD22",
         "*sD0","*sD1","*sD2","*S","*rho"], "BSSN/ID_TOV_BSSN_Source_Terms.h",outCparams)

with open("BSSN/ID_TOV_BSSN_Source_Terms.h", "a") as file:
    file.write("}\n")
```

    Appended to file "BSSN/ID_TOV_BSSN_Source_Terms.h"

# Step 3: Convert ADM spacetime quantity initial data from Spherical to BSSN Curvilinear coordinates \[Back to [top](#toc)\]
$$\label{adm_id_spacetime}$$

We convert ADM spacetime quantity initial data from Spherical to BSSN Curvilinear coordinates [as documented in the corresponding tutorial module](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb)

```python
import BSSN.ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoBnum
AtoBnum.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical","ID_TOV_ADM_quantities")
```

    Appended to file "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h"
    Appended to file "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h"

# Step 4: Validating that the TOV initial data satisfy the Hamiltonian constraint \[Back to [top](#toc)\]
$$\label{validate}$$

We will validate that the TOV initial data satisfy the Hamiltonian constraint, modulo numerical finite differencing error

## Step 4.a: Output the Hamiltonian constraint \[Back to [top](#toc)\]
$$\label{ham_const_output}$$

First output the Hamiltonian constraint [as documented in the corresponding NRPy+ tutorial module](Tutorial-BSSN_constraints.ipynb)

```python
import BSSN.BSSN_constraints as bssncon
bssncon.output_C__Hamiltonian_h(add_T4UUmunu_source_terms=True)
```

    Generating optimized C code for Hamiltonian constraint. May take a while, depending on CoordSystem.
    Finished in 10.2082049847 seconds.
    Output C implementation of Hamiltonian constraint to BSSN/Hamiltonian.h

## Step 4.b: Apply singular, curvilinear coordinate boundary conditions \[Back to [top](#toc)\]
$$\label{apply_bcs}$$

Next apply singular, curvilinear coordinate boundary conditions [as documented in the corresponding NRPy+ tutorial module](Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb).

```python
import CurviBoundaryConditions.CurviBoundaryConditions as cbcs
cbcs.Set_up_CurviBoundaryConditions()
```

    Wrote to file "CurviBoundaryConditions/gridfunction_defines.h"
    Wrote to file "CurviBoundaryConditions/set_parity_conditions.h"
    Wrote to file "CurviBoundaryConditions/xxCart.h"
    Wrote to file "CurviBoundaryConditions/xxminmax.h"
    Wrote to file "CurviBoundaryConditions/Cart_to_xx.h"

## Step 4.c: Enforce conformal 3-metric $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$ constraint \[Back to [top](#toc)\]
$$\label{enforce3metric}$$

Then enforce conformal 3-metric $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$ constraint (Eq. 53 of [Ruchlin, Etienne, and Baumgarte (2018)](https://arxiv.org/abs/1712.07658)), as [documented in the corresponding NRPy+ tutorial module](Tutorial-BSSN-Enforcing_Determinant_gammabar_equals_gammahat_Constraint.ipynb).

Applying curvilinear boundary conditions should affect the initial data at the outer boundary, and will in general cause the $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$ constraint to be violated there. Thus after we apply these boundary conditions, we must always call the routine for enforcing the $\det{\bar{\gamma}_{ij}}=\det{\hat{\gamma}_{ij}}$ constraint:

```python
import BSSN.Enforce_Detgammabar_Constraint as EGC
EGC.output_Enforce_Detgammabar_Constraint_Ccode()
```

    Output C implementation of det(gammabar) constraint to file BSSN/enforce_detgammabar_constraint.h

# Step 5: `TOV_Playground.c`: The Main C Code \[Back to [top](#toc)\]
$$\label{mainc}$$

```python
# Part P0: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER
with open("BSSN/NGHOSTS.h", "w") as file:
    file.write("// Part P0: Set the number of ghost zones, from NRPy+'s FD_CENTDERIVS_ORDER\n")
    # Upwinding in BSSN requires that NGHOSTS = FD_CENTDERIVS_ORDER/2 + 1 <- Notice the +1.
    file.write("#define NGHOSTS "+str(int(par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER")/2)+1)+"\n")
```

```python
%%writefile BSSN/TOV_Playground.c

// Part P1: Import needed header files
#include "NGHOSTS.h" // A NRPy+-generated file, which is set based on FD_CENTDERIVS_ORDER.
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "stdint.h" // Needed for Windows GCC 6.x compatibility

// Part P2: Add needed #define's to set data type, the IDX4() macro, and the gridfunctions
// Part P2a: set REAL=double, so that all floating point numbers are stored to at least ~16 significant digits.
#define REAL double

// Step P3: Set free parameters for the numerical grid
const REAL RMAX    = 3.0;

typedef struct __ID_inputs {
    REAL Rbar;
    int Rbar_idx;
    int interp_stencil_size;
    int numlines_in_file;
    REAL *r_Schw_arr,*rho_arr,*P_arr,*M_arr,*expnu_arr,*exp4phi_arr,*rbar_arr;
} ID_inputs;

// Part P4b: Declare the IDX4(gf,i,j,k) macro, which enables us to store 4-dimensions of
//           data in a 1D array. In this case, consecutive values of "i"
//           (all other indices held to a fixed value) are consecutive in memory, where
//           consecutive values of "j" (fixing all other indices) are separated by
//           Nxx_plus_2NGHOSTS[0] elements in memory. Similarly, consecutive values of
//           "k" are separated by Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1] in memory, etc.
#define IDX4(g,i,j,k) \
( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * ( (k) + Nxx_plus_2NGHOSTS[2] * (g) ) ) )
#define IDX3(i,j,k) ( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * (k) ) )
// Assuming idx = IDX3(i,j,k). Much faster if idx can be reused over and over:
#define IDX4pt(g,idx)   ( (idx) + (Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2]) * (g) )

// Part P4c: Set #define's for BSSN gridfunctions. C code generated above
#include "../CurviBoundaryConditions/gridfunction_defines.h"

#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) \
  for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)

// Step P5: Function for converting uniform grid coord
//         (xx[0][i0],xx[1][i1],xx[2][i2]) to
//          corresponding Cartesian coordinate.
void xxCart(REAL *xx[3],const int i0,const int i1,const int i2, REAL xCart[3]) {
    REAL xx0 = xx[0][i0];
    REAL xx1 = xx[1][i1];
    REAL xx2 = xx[2][i2];
#include "xxCart.h"
}

// Step P6: Include basic functions needed to impose curvilinear
//          parity and boundary conditions.
#include "../CurviBoundaryConditions/curvilinear_parity_and_outer_boundary_conditions.h"

// Step P7: Function for enforcing the gammabar=gammahat constraint:
#include "enforce_detgammabar_constraint.h"

// Part P8: Declare all functions for setting up UIUC initial data.
/* Routines to interpolate the TOV solution and convert to ADM & T^{munu}: */
#include "../TOV/tov_interp.h"
#include "ID_TOV_ADM_quantities.h"
#include "ID_TOV_TUPMUNU.h"

/* Next perform the basis conversion and compute all needed BSSN quantities */
#include "ID_TOV_BSSN_Source_Terms.h"
#include "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h"
#include "ID_BSSN__ALL_BUT_LAMBDAs.h"
#include "ID_BSSN_lambdas.h"

// Part P9: Declare function for computing the Hamiltonian
//           constraint violation, which should converge to
//           zero with increasing numerical resolution.
void Hamiltonian_constraint(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],const REAL dxx[3], REAL *xx[3],
                            REAL *in_gfs, REAL *aux_gfs) {
#include "Hamiltonian.h"
}

// main() function:
// Step 0: Read command-line input, set up grid structure, allocate memory for gridfunctions, set up coordinates
// Step 1: Set up scalar wave initial data
// Step 2: Evolve scalar wave initial data forward in time using Method of Lines with RK4 algorithm,
//         applying quadratic extrapolation outer boundary conditions.
// Step 3: Output relative error between numerical and exact solution.
// Step 4: Free all allocated memory
int main(int argc, const char *argv[]) {
    // Step 0a: Read command-line input, error out if nonconformant
    if(argc != 4 || atoi(argv[1]) < NGHOSTS) {
        fprintf(stderr,"Error: Expected three command-line arguments: ./TOV_Playground Nx0 Nx1 Nx2,\n");
        fprintf(stderr,"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.\n");
        fprintf(stderr,"Nx[] MUST BE larger than NGHOSTS (= %d)\n",NGHOSTS);
        exit(1);
    }
    // Step 0b: Set up numerical grid structure, first in space...
    if(atoi(argv[1])%2 != 0 || atoi(argv[2])%2 != 0 || atoi(argv[2])%2 != 0) {
        fprintf(stderr,"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.\n");
        fprintf(stderr,"       For example, in case of angular directions, proper symmetry zones will not exist.\n");
        exit(1);
    }
    const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };
    const int Nxx_plus_2NGHOSTS[3] = { Nxx[0]+2*NGHOSTS, Nxx[1]+2*NGHOSTS, Nxx[2]+2*NGHOSTS };
    const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2];
#include "xxminmax.h"

    /* TOV INPUT ROUTINE */
    // Open the data file:
    char filename[100];
    sprintf(filename,"./outputTOVpolytrope.txt");
    FILE *in1Dpolytrope = fopen(filename, "r");
    if (in1Dpolytrope == NULL) {
        fprintf(stderr,"ERROR: could not open file %s\n",filename);
        exit(1);
    }
    // Count the number of lines in the data file:
    int numlines_in_file = count_num_lines_in_file(in1Dpolytrope);
    // Allocate space for all data arrays:
    REAL *r_Schw_arr  = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *rho_arr     = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *P_arr       = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *M_arr       = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *expnu_arr   = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *exp4phi_arr = (REAL *)malloc(sizeof(REAL)*numlines_in_file);
    REAL *rbar_arr    = (REAL *)malloc(sizeof(REAL)*numlines_in_file);

    // Read from the data file, filling in arrays
    if(read_datafile__set_arrays(in1Dpolytrope, r_Schw_arr,rho_arr,P_arr,M_arr,expnu_arr,exp4phi_arr,rbar_arr) == 1) {
        fprintf(stderr,"ERROR WHEN READING FILE %s!\n",filename);
        exit(1);
    }
    fclose(in1Dpolytrope);
    REAL Rbar = -100;
    int Rbar_idx = -100;
    for(int i=1;i<numlines_in_file;i++) {
        if(rho_arr[i-1]>0 && rho_arr[i]==0) { Rbar = rbar_arr[i-1]; Rbar_idx = i-1; }
    }
    if(Rbar<0) {
        fprintf(stderr,"Error: could not find rbar=Rbar from data file.\n");
        exit(1);
    }

    ID_inputs TOV_in;
    TOV_in.Rbar = Rbar;
    TOV_in.Rbar_idx = Rbar_idx;

    const int interp_stencil_size = 12;
    TOV_in.interp_stencil_size = interp_stencil_size;
    TOV_in.numlines_in_file = numlines_in_file;

    TOV_in.r_Schw_arr = r_Schw_arr;
    TOV_in.rho_arr = rho_arr;
    TOV_in.P_arr = P_arr;
    TOV_in.M_arr = M_arr;
    TOV_in.expnu_arr = expnu_arr;
    TOV_in.exp4phi_arr = exp4phi_arr;
    TOV_in.rbar_arr = rbar_arr;
    /* END TOV INPUT ROUTINE */

    // Step 0c: Allocate memory for gridfunctions
    REAL *evol_gfs = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot);
    REAL *aux_gfs  = (REAL *)malloc(sizeof(REAL) * NUM_AUX_GFS * Nxx_plus_2NGHOSTS_tot);

    // Step 0d: Set up space and time coordinates
    // Step 0d.i: Set \Delta x^i on uniform grids.
    REAL dxx[3];
    for(int i=0;i<3;i++) dxx[i] = (xxmax[i] - xxmin[i]) / ((REAL)Nxx[i]);

    // Step 0d.ii: Set up uniform coordinate grids
    REAL *xx[3];
    for(int i=0;i<3;i++) {
        xx[i] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS[i]);
        for(int j=0;j<Nxx_plus_2NGHOSTS[i];j++) {
            xx[i][j] = xxmin[i] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*dxx[i]; // Cell-centered grid.
        }
    }

    // Step 0e: Find ghostzone mappings and parities:
    gz_map *bc_gz_map = (gz_map *)malloc(sizeof(gz_map)*Nxx_plus_2NGHOSTS_tot);
    parity_condition *bc_parity_conditions = (parity_condition *)malloc(sizeof(parity_condition)*Nxx_plus_2NGHOSTS_tot);
    set_up_bc_gz_map_and_parity_conditions(Nxx_plus_2NGHOSTS,xx,dxx,xxmin,xxmax,  bc_gz_map, bc_parity_conditions);

    // Step 1: Set up initial data to an exact solution at time=0:
    ID_BSSN__ALL_BUT_LAMBDAs(Nxx_plus_2NGHOSTS,xx,TOV_in, evol_gfs);
    apply_bcs(Nxx, Nxx_plus_2NGHOSTS, bc_gz_map,bc_parity_conditions,NUM_EVOL_GFS,evol_gf_parity, evol_gfs);
    enforce_detgammabar_constraint(Nxx_plus_2NGHOSTS, xx, evol_gfs);
    ID_BSSN_lambdas(Nxx, Nxx_plus_2NGHOSTS, xx,dxx, evol_gfs);
    apply_bcs(Nxx, Nxx_plus_2NGHOSTS, bc_gz_map,bc_parity_conditions,NUM_EVOL_GFS,evol_gf_parity, evol_gfs);
    enforce_detgammabar_constraint(Nxx_plus_2NGHOSTS, xx, evol_gfs);
{
    #pragma omp parallel for
    for(int i2=0; i2<Nxx_plus_2NGHOSTS[2]; i2++) {
        const REAL xx2 = xx[2][i2];
        for(int i1=0; i1<Nxx_plus_2NGHOSTS[1]; i1++) {
            const REAL xx1 = xx[1][i1];
            for(int i0=0; i0<Nxx_plus_2NGHOSTS[0]; i0++) {
                const REAL xx0 = xx[0][i0];
                REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
                REAL gammaDD00,gammaDD01,gammaDD02,gammaDD11,gammaDD12,gammaDD22;
                REAL KDD00,KDD01,KDD02,KDD11,KDD12,KDD22;
                REAL alpha,betaU0,betaU1,betaU2;
                REAL BU0,BU1,BU2;
                REAL xyz_or_rthph[3] =  {xx0,xx1,xx2}; //FIXME

                // FIRST INTERPOLATE TO SET THE ADM AND TMUNU QUANTITIES
                ID_TOV_ADM_quantities(xyz_or_rthph, TOV_in,

                    &gammaDD00,&gammaDD01,&gammaDD02,&gammaDD11,&gammaDD12,&gammaDD22,
                    &KDD00,&KDD01,&KDD02,&KDD11,&KDD12,&KDD22,
                    &alpha,  &betaU0,&betaU1,&betaU2,  &BU0,&BU1,&BU2);

                REAL T4UU00,T4UU01,T4UU02,T4UU03,
                /**/        T4UU11,T4UU12,T4UU13,
                /**/               T4UU22,T4UU23,
                /**/                      T4UU33;

                ID_TOV_TUPMUNU(xyz_or_rthph, TOV_in,

                    &T4UU00,&T4UU01,&T4UU02,&T4UU03,
                    /**/    &T4UU11,&T4UU12,&T4UU13,
                    /**/            &T4UU22,&T4UU23,
                    /**/                    &T4UU33);

                const int idx = IDX3(i0,i1,i2);
                // THEN EVALUATE THE BSSN SOURCE TERMS
                ID_TOV_BSSN_Source_Terms(xx0xx1xx2,
                                         gammaDD00, gammaDD01, gammaDD02,
                    /**/                            gammaDD11, gammaDD12,
                    /**/                                       gammaDD22,

                    betaU0,betaU1,betaU2, alpha,

                    T4UU00, T4UU01, T4UU02, T4UU03,
                    /**/    T4UU11, T4UU12, T4UU13,
                    /**/            T4UU22, T4UU23,
                    /**/                    T4UU33,

                    &aux_gfs[IDX4pt(SDD00GF,idx)], &aux_gfs[IDX4pt(SDD01GF,idx)], &aux_gfs[IDX4pt(SDD02GF,idx)],
                    /**/                           &aux_gfs[IDX4pt(SDD11GF,idx)], &aux_gfs[IDX4pt(SDD12GF,idx)],
                    /**/                                                          &aux_gfs[IDX4pt(SDD22GF,idx)],

                    &aux_gfs[IDX4pt(SD0GF,idx)], &aux_gfs[IDX4pt(SD1GF,idx)], &aux_gfs[IDX4pt(SD2GF,idx)],

                    &aux_gfs[IDX4pt(SGF,idx)], &aux_gfs[IDX4pt(RHOGF,idx)]);
                //aux_gfs[IDX4pt(SGF,idx)] = T4UU11*gammaDD00;
            }
        }
    }
}

    // Step 1b: Apply boundary conditions, as initial data
    //          are sometimes ill-defined in ghost zones.
    //          E.g., spherical initial data might not be
    //          properly defined at points where r=-1.
//    apply_bcs(Nxx, Nxx_plus_2NGHOSTS, bc_gz_map,bc_parity_conditions,NUM_EVOL_GFS,evol_gf_parity, evol_gfs);
//    enforce_detgammabar_constraint(Nxx_plus_2NGHOSTS, xx, evol_gfs);

    // Step 2: Evaluate Hamiltonian constraint violation
    Hamiltonian_constraint(Nxx,Nxx_plus_2NGHOSTS,dxx, xx, evol_gfs, aux_gfs);

    /* Step 3: Output relative error between numerical and exact solution, */
    const int i0MIN=NGHOSTS; // In spherical, r=Delta r/2.
    const int i1mid=Nxx_plus_2NGHOSTS[1]/2;
    const int i2mid=Nxx_plus_2NGHOSTS[2]/2;
    LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS[0]-NGHOSTS, i1mid,i1mid+1, NGHOSTS,Nxx_plus_2NGHOSTS[2]-NGHOSTS) {
        REAL xx0 = xx[0][i0];
        REAL xx1 = xx[1][i1];
        REAL xx2 = xx[2][i2];
        REAL xCart[3];
#include "xxCart.h"
        int idx = IDX3(i0,i1,i2);
        printf("%e %e %e %e\n",xCart[0],xCart[1], aux_gfs[IDX4pt(SGF,idx)],log10(fabs(aux_gfs[IDX4pt(HGF,idx)])));
    }

    /* Step 4: Free all allocated memory */
    free(bc_gz_map);
    free(bc_parity_conditions);
    free(rbar_arr);
    free(rho_arr);
    free(P_arr);
    free(M_arr);
    free(expnu_arr);

    free(aux_gfs);
    free(evol_gfs);
    for(int i=0;i<3;i++) free(xx[i]);
    return 0;
}
```

    Overwriting BSSN/TOV_Playground.c

```python
import cmdline_helper as cmd
cmd.C_compile("BSSN/TOV_Playground.c", "TOV_Playground")
cmd.delete_existing_files("out96.txt")
cmd.Execute("TOV_Playground", "96 96 96", "out96.txt")
```

    Compiling executable...
    Executing `gcc -Ofast -fopenmp -march=native -funroll-loops BSSN/TOV_Playground.c -o TOV_Playground -lm`...
    Finished executing in 3.83846592903 seconds.
    Finished compilation.
    Executing `taskset -c 0,1,2,3 ./TOV_Playground 96 96 96`...
    Finished executing in 1.03296709061 seconds.

# Step 6: Plotting the single-neutron-star initial data \[Back to [top](#toc)\]
$$\label{plot}$$

Here we plot the conformal factor of these initial data on a 2D grid, such that darker colors imply stronger gravitational fields. Hence, we see the single neutron star centered at the origin: $x/M=y/M=z/M=0$, where $M$ is an arbitrary mass scale (conventionally the [ADM mass](https://en.wikipedia.org/w/index.php?title=ADM_formalism&oldid=846335453) is chosen), and our formulation of Einstein's equations adopt $G=c=1$ [geometrized units](https://en.wikipedia.org/w/index.php?title=Geometrized_unit_system&oldid=861682626).

```python
import numpy as np
from scipy.interpolate import griddata
from pylab import savefig
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from IPython.display import Image

x96,y96,valuesCF96,valuesHam96 = np.loadtxt('out96.txt').T #Transposed for easier unpacking

bounds = 2.0
pl_xmin = -bounds
pl_xmax = +bounds
pl_ymin = -bounds
pl_ymax = +bounds

grid_x, grid_y = np.mgrid[pl_xmin:pl_xmax:100j, pl_ymin:pl_ymax:100j]
points96 = np.zeros((len(x96), 2))
for i in range(len(x96)):
    points96[i][0] = x96[i]
    points96[i][1] = y96[i]

grid96 = griddata(points96, valuesCF96, (grid_x, grid_y), method='nearest')
grid96cub = griddata(points96, valuesCF96, (grid_x, grid_y), method='cubic')

plt.clf()
plt.title("TOV Polytrope Initial Data: log10(Density)")
plt.xlabel("x/M")
plt.ylabel("y/M")

# fig, ax = plt.subplots()
# ax.plot(grid96cub.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))
# plt.close(fig)
fig96cf = plt.imshow(grid96.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))
cb = plt.colorbar(fig96cf)
savefig("BHB.png")
from IPython.display import Image
Image("BHB.png")
# #           interpolation='nearest', cmap=cm.gist_rainbow)
```

# Step 7: Validation: Convergence of numerical errors (Hamiltonian constraint violation) to zero \[Back to [top](#toc)\]
$$\label{convergence}$$

The equations behind these initial data solve Einstein's equations exactly, at a single instant in time. One reflection of this solution is that the Hamiltonian constraint violation should be exactly zero in the initial data.

However, when evaluated on numerical grids, the Hamiltonian constraint violation will *not* generally evaluate to zero due to the associated numerical derivatives not being exact. However, these numerical derivatives (finite difference derivatives in this case) should *converge* to the exact derivatives as the density of numerical sampling points approaches infinity.

In this case, all of our finite difference derivatives agree with the exact solution, with an error term that drops with the uniform gridspacing to the fourth power: $\left(\Delta x^i\right)^4$.

Here, as in the [Start-to-Finish Scalar Wave (Cartesian grids) NRPy+ tutorial](Tutorial-Start_to_Finish-ScalarWave.ipynb) and the [Start-to-Finish Scalar Wave (curvilinear grids) NRPy+ tutorial](Tutorial-Start_to_Finish-ScalarWaveCurvilinear.ipynb) we confirm this convergence.

First, let's take a look at what the numerical error looks like on the x-y plane at a given numerical resolution, plotting $\log_{10}|H|$, where $H$ is the Hamiltonian constraint violation:

```python
grid96 = griddata(points96, valuesHam96, (grid_x, grid_y), method='nearest')
grid96cub = griddata(points96, valuesHam96, (grid_x, grid_y), method='cubic')

# fig, ax = plt.subplots()

plt.clf()
plt.title("96^3 Numerical Err.: log_{10}|Ham|")
plt.xlabel("x/M")
plt.ylabel("y/M")

fig96cub = plt.imshow(grid96cub.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))
cb = plt.colorbar(fig96cub)
```

Next, we set up the same initial data but on a lower-resolution, $48\times 8\times 2$ grid (axisymmetric in the $\phi$ direction). Since the constraint violation (numerical error associated with the fourth-order-accurate, finite-difference derivatives) should converge to zero with the uniform gridspacing to the fourth power: $\left(\Delta x^i\right)^4$, we expect the constraint violation will increase (relative to the $96\times 16\times 2$ grid) by a factor of $\left(96/48\right)^4$. Here we demonstrate that indeed this order of convergence is observed as expected, *except* at the star's surface where the stress-energy tensor $T^{\mu\nu}$ sharply drops to zero.

```python
# Now rerun TOV_Playground with twice lower resolution.
cmd.delete_existing_files("out48.txt")
cmd.Execute("TOV_Playground", "48 48 48", "out48.txt")

x48,y48,valuesCF48,valuesHam48 = np.loadtxt('out48.txt').T #Transposed for easier unpacking
points48 = np.zeros((len(x48), 2))
for i in range(len(x48)):
    points48[i][0] = x48[i]
    points48[i][1] = y48[i]

grid48 = griddata(points48, valuesHam48, (grid_x, grid_y), method='cubic')

griddiff_48_minus_96 = np.zeros((100,100))
griddiff_48_minus_96_1darray = np.zeros(100*100)
gridx_1darray_yeq0 = np.zeros(100)
grid48_1darray_yeq0 = np.zeros(100)
grid96_1darray_yeq0 = np.zeros(100)
count = 0
outarray = []
for i in range(100):
    for j in range(100):
        griddiff_48_minus_96[i][j] = grid48[i][j] - grid96[i][j]
        griddiff_48_minus_96_1darray[count] = griddiff_48_minus_96[i][j]
        if j==49:
            gridx_1darray_yeq0[i] = grid_x[i][j]
            grid48_1darray_yeq0[i] = grid48[i][j] + np.log10((48./96.)**4)
            grid96_1darray_yeq0[i] = grid96[i][j]
        count = count + 1

plt.clf()
fig, ax = plt.subplots()
plt.title("Plot Demonstrating 4th-order Convergence")
plt.xlabel("x/M")
plt.ylabel("log10(Relative error)")

ax.plot(gridx_1darray_yeq0, grid96_1darray_yeq0, 'k-', label='Nr=96')
ax.plot(gridx_1darray_yeq0, grid48_1darray_yeq0, 'k--', label='Nr=48, mult by (48/96)^4')
ax.set_ylim([-12.5,1.5])

legend = ax.legend(loc='lower right', shadow=True, fontsize='x-large')
legend.get_frame().set_facecolor('C1')
plt.show()
```

# Step 8: Output this module 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-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.pdf](Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)

```python
!jupyter nbconvert --to latex --template latex_nrpy_style.tplx Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.ipynb
!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.tex
!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.tex
!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```

    [NbConvertApp] Converting notebook Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.ipynb to latex
    [NbConvertApp] Support files will be in Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data_files/
    [NbConvertApp] Making directory Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data_files
    [NbConvertApp] Making directory Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data_files
    [NbConvertApp] Making directory Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data_files
    [NbConvertApp] Writing 113436 bytes to Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_TOV_initial_data.tex
    This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
     restricted \write18 enabled.
    entering extended mode
    This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
     restricted \write18 enabled.
    entering extended mode
    This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)
     restricted \write18 enabled.
    entering extended mode


## Histograms of Oriented Gradients (HOG)

As we saw with the ORB algorithm, we can use keypoints in images to do keypoint-based matching to detect objects in images. These type of algorithms work great when you want to detect objects that have a lot of consistent internal features that are not affected by the background. For example, these algorithms work well for facial detection because faces have a lot of consistent internal features that don’t get affected by the image background, such as the eyes, nose, and mouth. However, these type of algorithms don’t work so well when attempting to do more general object recognition, say for example, pedestrian detection in images. The reason is that people don’t have consistent internal features, like faces do, because the body shape and style of every person is different (see Fig. 1). This means that every person is going to have a different set of internal features, and so we need something that can more generally describe a person.

Fig. 1. - Pedestrians.

One option is to try to detect pedestrians by their contours instead.  Detecting objects in images by their contours (boundaries) is very challenging because we have to deal with the difficulties brought about by the contrast between the background and the foreground. For example, suppose you wanted to detect a pedestrian in an image that is walking in front of a white building and she is wearing a white coat and black pants (see Fig. 2). We can see in Fig. 2, that since the background of the image is mostly white, the black pants are going to have a very high contrast, but the coat, since it is white as well, is going to have very low contrast. In this case, detecting the edges of pants is going to be easy but detecting the edges of the coat is going to be very difficult. This is where **HOG** comes in. HOG stands for **Histograms of Oriented Gradients** and it was first introduced by Navneet Dalal and Bill Triggs in 2005.

Fig. 2. - High and Low Contrast.

The HOG algorithm works by creating histograms of the distribution of gradient orientations in an image and then normalizing them in a very special way. This special normalization is what makes HOG so effective at detecting the edges of objects even in cases where the contrast is very low. These normalized histograms are put together into a feature vector, known as the HOG descriptor, that can be used to train a machine learning algorithm, such as a Support Vector Machine (SVM), to detect objects in images based on their boundaries (edges). Due to its great success and reliability, HOG has become one of the most widely used algorithms in computer vison for object detection.

In this notebook, you will learn:

* How the HOG algorithm works
* How to use OpenCV to create a HOG descriptor
* How to visualize the HOG descriptor.

# The HOG Algorithm

As its name suggests, the HOG algorithm, is based on creating histograms from the orientation of image gradients. The HOG algorithm is implemented in a series of steps:

1. Given the image of particular object, set a detection window (region of interest) that covers the entire object in the image (see Fig. 3).

2. Calculate the magnitude and direction of the gradient for each individual pixel in the detection window.

3. Divide the detection window into connected *cells* of pixels, with all cells being of the same size (see Fig. 3). The size of the cells is a free parameter and it is usually chosen so as to match the scale of the features that want to be detected. For example, in a 64 x 128 pixel detection window, square cells 6 to 8 pixels wide are suitable for detecting human limbs.

4. Create a Histogram for each cell, by first grouping the gradient directions of all pixels in each cell into a particular number of orientation (angular) bins; and then adding up the gradient magnitudes of the gradients in each angular bin (see Fig. 3). The number of bins in the histogram is a free parameter and it is usually set to 9 angular bins.

5. Group adjacent cells into *blocks* (see Fig. 3). The number of cells in each block is a free parameter and all blocks must be of the same size. The distance between each block (known as the stride) is a free parameter but it is usually set to half the block size, in which case you will get overlapping blocks (*see video below*).  The HOG algorithm has been shown empirically to work better with overlapping blocks.

6. Use the cells contained within each block to normalize the cell histograms in that block (see Fig. 3). If you have overlapping blocks this means that most cells will be normalized with respect to different blocks (*see video below*). Therefore, the same cell may have several different normalizations.

7. Collect all the normalized histograms from all the blocks into a single feature vector called the HOG descriptor.

8. Use the resulting HOG descriptors from many images of the same type of object to train a machine learning algorithm, such as an SVM, to detect those type of objects in images. For example, you could use the HOG descriptors from many images of pedestrians to train an SVM to detect pedestrians in images. The training is done with both positive a negative examples of the object you want detect in the image.

9. Once the SVM has been trained, a sliding window approach is used to try to detect and locate objects in images.  Detecting an object in the image entails finding the part of the image that looks similar to the HOG pattern learned by the SVM.

Fig. 3. - HOG Diagram.

Vid. 1. - HOG Animation.

# Why The HOG Algorithm Works

As we learned above, HOG creates histograms by adding the magnitude of the gradients in particular orientations in localized portions of the image called *cells*.  By doing this we guarantee that stronger gradients will contribute more to the magnitude of their respective angular bin, while the effects of weak and randomly oriented gradients resulting from noise are minimized. In this manner the histograms tell us the dominant gradient orientation of each cell.

### Dealing with contrast

Now, the magnitude of the dominant orientation can vary widely due to variations in local illumination and the contrast between the background and the foreground.

To account for the background-foreground contrast differences, the HOG algorithm tries to detect edges locally.  In order to do this, it defines groups of cells, called **blocks**, and normalizes the histograms using this local group of cells. By normalizing locally, the HOG algorithm can detect the edges in each block very reliably; this is called **block normalization**.

In addition to using block normalization, the HOG algorithm also uses overlapping blocks to increase its performance. By using overlapping blocks, each cell contributes several independent components to the final HOG descriptor, where each component corresponds to a cell being normalized with respect to a different block. This may seem redundant but, it has been shown empirically that by normalizing each cell several times with respect to different local blocks, the performance of the HOG algorithm increases dramatically.

### Loading Images and Importing Resources

The first step in building our HOG descriptor is to load the required packages into Python and to load our image.

We start by using OpenCV to load an image of a triangle tile. Since, the `cv2.imread()` function loads images as BGR we will convert our image to RGB so we can display it with the correct colors. As usual we will convert our BGR image to Gray Scale for analysis.

```python
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Set the default figure size
plt.rcParams['figure.figsize'] = [17.0, 7.0]

# Load the image
image = cv2.imread('./images/triangle_tile.jpeg')

# Convert the original image to RGB
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Convert the original image to gray scale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Print the shape of the original and gray scale images
print('The original image has shape: ', original_image.shape)
print('The gray scale image has shape: ', gray_image.shape)

# Display the images
plt.subplot(121)
plt.imshow(original_image)
plt.title('Original Image')
plt.subplot(122)
plt.imshow(gray_image, cmap='gray')
plt.title('Gray Scale Image')
plt.show()
```

    The original image has shape:  (250, 250, 3)
    The gray scale image has shape:  (250, 250)

    <matplotlib.figure.Figure at 0x7fb4e4548390>

# Creating The HOG Descriptor

We will be using OpenCV’s `HOGDescriptor` class to create the HOG descriptor. The parameters of the HOG descriptor are setup using the `HOGDescriptor()` function. The parameters of the `HOGDescriptor()` function and their default values are given below:

`cv2.HOGDescriptor(win_size = (64, 128),
                  block_size = (16, 16),
                  block_stride = (8, 8),
                  cell_size = (8, 8),
                  nbins = 9,
                  win_sigma = DEFAULT_WIN_SIGMA,
                  threshold_L2hys = 0.2,
                  gamma_correction = true,
                  nlevels = DEFAULT_NLEVELS)`

Parameters:

* **win_size** – *Size*
Size of detection window in pixels (*width, height*). Defines the region of interest. Must be an integer multiple of cell size.

* **block_size** – *Size*
Block size in pixels (*width, height*). Defines how many cells are in each block. Must be an integer multiple of cell size and it must be smaller than the detection window. The smaller the block the finer detail you will get.

* **block_stride** – *Size*
Block stride in pixels (*horizontal, vertical*). It must be an integer multiple of cell size. The `block_stride` defines the distance between adjecent blocks, for example, 8 pixels horizontally and 8 pixels vertically. Longer `block_strides` makes the algorithm run faster (because less blocks are evaluated) but the algorithm may not perform as well.

* **cell_size** – *Size*
Cell size in pixels (*width, height*). Determines the size fo your cell. The smaller the cell the finer detail you will get.

* **nbins** – *int*
Number of bins for the histograms. Determines the number of angular bins used to make the histograms. With more bins you capture more gradient directions. HOG uses unsigned gradients, so the angular bins will have values between 0 and 180 degrees.

* **win_sigma** – *double*
Gaussian smoothing window parameter. The performance of the HOG algorithm can be improved by smoothing the pixels near the edges of the blocks by applying a Gaussian spatial window to each pixel before computing the histograms.

* **threshold_L2hys** – *double*
L2-Hys (Lowe-style clipped L2 norm) normalization method shrinkage. The L2-Hys method is used to normalize the blocks and it consists of an L2-norm followed by clipping and a renormalization. The clipping limits the maximum value of the descriptor vector for each block to have the value of the given threshold (0.2 by default). After the clipping the descriptor vector is renormalized as described in *IJCV*, 60(2):91-110, 2004.

* **gamma_correction** – *bool*
Flag to specify whether the gamma correction preprocessing is required or not. Performing gamma correction slightly increases the performance of the HOG algorithm.

* **nlevels** – *int*
Maximum number of detection window increases.

As we can see, the  `cv2.HOGDescriptor()`function supports a wide range of parameters. The first few arguments (`block_size, block_stride, cell_size`, and `nbins`) are probably the ones you are most likely to change. The other parameters can be safely left at their default values and you will get good results.

In the code below, we will use the `cv2.HOGDescriptor()`function to set the cell size, block size, block stride, and the number of bins for the histograms of the HOG descriptor. We will then use `.compute(image)`method to compute the HOG descriptor (feature vector) for the given `image`.

```python
# Specify the parameters for our HOG descriptor

# Cell Size in pixels (width, height). Must be smaller than the size of the detection window
# and must be chosen so that the resulting Block Size is smaller than the detection window.
cell_size = (6, 6)

# Number of cells per block in each direction (x, y). Must be chosen so that the resulting
# Block Size is smaller than the detection window
num_cells_per_block = (2, 2)

# Block Size in pixels (width, height). Must be an integer multiple of Cell Size.
# The Block Size must be smaller than the detection window
block_size = (num_cells_per_block[0] * cell_size[0],
              num_cells_per_block[1] * cell_size[1])

# Calculate the number of cells that fit in our image in the x and y directions
x_cells = gray_image.shape[1] // cell_size[0]
y_cells = gray_image.shape[0] // cell_size[1]

# Horizontal distance between blocks in units of Cell Size. Must be an integer and it must
# be set such that (x_cells - num_cells_per_block[0]) / h_stride = integer.
h_stride = 1

# Vertical distance between blocks in units of Cell Size. Must be an integer and it must
# be set such that (y_cells - num_cells_per_block[1]) / v_stride = integer.
v_stride = 1

# Block Stride in pixels (horizantal, vertical). Must be an integer multiple of Cell Size
block_stride = (cell_size[0] * h_stride, cell_size[1] * v_stride)

# Number of gradient orientation bins
num_bins = 9

# Specify the size of the detection window (Region of Interest) in pixels (width, height).
# It must be an integer multiple of Cell Size and it must cover the entire image. Because
# the detection window must be an integer multiple of cell size, depending on the size of
# your cells, the resulting detection window might be slightly smaller than the image.
# This is perfectly ok.
win_size = (x_cells * cell_size[0] , y_cells * cell_size[1])

# Print the shape of the gray scale image for reference
print('\nThe gray scale image has shape: ', gray_image.shape)
print()

# Print the parameters of our HOG descriptor
print('HOG Descriptor Parameters:\n')
print('Window Size:', win_size)
print('Cell Size:', cell_size)
print('Block Size:', block_size)
print('Block Stride:', block_stride)
print('Number of Bins:', num_bins)
print()

# Set the parameters of the HOG descriptor using the variables defined above
hog = cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, num_bins)

# Compute the HOG Descriptor for the gray scale image
hog_descriptor = hog.compute(gray_image)
```

    The gray scale image has shape:  (250, 250)

    HOG Descriptor Parameters:

    Window Size: (246, 246)
    Cell Size: (6, 6)
    Block Size: (12, 12)
    Block Stride: (6, 6)
    Number of Bins: 9

# Number of Elements In The HOG Descriptor

The resulting HOG Descriptor (feature vector), contains the normalized histograms from all cells from all blocks in the detection window concatenated in one long vector. Therefore, the size of the HOG feature vector will be given by the total number of blocks in the detection window, multiplied by the number of cells per block, times the number of orientation bins:

\begin{equation}
\mbox{total_elements} = (\mbox{total_number_of_blocks})\mbox{ } \times \mbox{ } (\mbox{number_cells_per_block})\mbox{ } \times \mbox{ } (\mbox{number_of_bins})
\end{equation}

If we don’t have overlapping blocks (*i.e.* the `block_stride`equals the `block_size`), the total number of blocks can be easily calculated by dividing the size of the detection window by the block size. However, in the general case we have to take into account the fact that we have overlapping blocks. To find the total number of blocks in the general case (*i.e.* for any `block_stride` and `block_size`), we can use the formula given below:

\begin{equation}
\mbox{Total}_i = \left( \frac{\mbox{block_size}_i}{\mbox{block_stride}_i} \right)\left( \frac{\mbox{window_size}_i}{\mbox{block_size}_i} \right) - \left [\left( \frac{\mbox{block_size}_i}{\mbox{block_stride}_i} \right) - 1 \right]; \mbox{  for  } i = x,y
\end{equation}

Where Total$_x$, is the total number of blocks along the width of the detection window, and Total$_y$, is the total number of blocks along the height of the detection window. This formula for Total$_x$ and Total$_y$, takes into account the extra blocks that result from overlapping. After calculating Total$_x$ and Total$_y$, we can get the total number of blocks in the detection window by multiplying Total$_x$  $\times$ Total$_y$. The above formula can be simplified considerably because the `block_size`, `block_stride`, and `window_size`are all defined in terms of the `cell_size`. By making all the appropriate substitutions and cancelations the above formula reduces to:

\begin{equation}
\mbox{Total}_i = \left(\frac{\mbox{cells}_i - \mbox{num_cells_per_block}_i}{N_i}\right) + 1\mbox{  }; \mbox{  for  } i = x,y
\end{equation}

Where cells$_x$ is the total number of cells along the width of the detection window, and cells$_y$, is the total number of cells along the height of the detection window. And $N_x$ is the horizontal block stride in units of `cell_size` and $N_y$ is the vertical block stride in units of `cell_size`.

Let's calculate what the number of elements for the HOG feature vector should be and check that it matches the shape of the HOG Descriptor calculated above.

```python
# Calculate the total number of blocks along the width of the detection window
tot_bx = np.uint32(((x_cells - num_cells_per_block[0]) / h_stride) + 1)

# Calculate the total number of blocks along the height of the detection window
tot_by = np.uint32(((y_cells - num_cells_per_block[1]) / v_stride) + 1)

# Calculate the total number of elements in the feature vector
tot_els = (tot_bx) * (tot_by) * num_cells_per_block[0] * num_cells_per_block[1] * num_bins

# Print the total number of elements the HOG feature vector should have
print('\nThe total number of elements in the HOG Feature Vector should be: ',
      tot_bx, 'x',
      tot_by, 'x',
      num_cells_per_block[0], 'x',
      num_cells_per_block[1], 'x',
      num_bins, '=',
      tot_els)

# Print the shape of the HOG Descriptor to see that it matches the above
print('\nThe HOG Descriptor has shape:', hog_descriptor.shape)
print()
```

    The total number of elements in the HOG Feature Vector should be:  40 x 40 x 2 x 2 x 9 = 57600

    The HOG Descriptor has shape: (57600, 1)

# Visualizing The HOG Descriptor

We can visualize the HOG Descriptor by plotting the histogram associated with each cell as a collection of vectors. To do this, we will plot each bin in the histogram as a single vector whose magnitude is given by the height of the bin and its orientation is given by the angular bin that its associated with. Since any given cell might have multiple histograms associated with it, due to the overlapping blocks, we will choose to average all the histograms for each cell to produce a single histogram for each cell.

OpenCV has no easy way to visualize the HOG Descriptor, so we have to do some manipulation first in order to visualize it. We will start by reshaping the HOG Descriptor in order to make our calculations easier.  We will then compute the average histogram of each cell and finally we will convert the histogram bins into vectors. Once we have the vectors, we plot the corresponding vectors for each cell in an image.

The code below produces an interactive plot so that you can interact with the figure. The figure contains:
* the grayscale image,
* the HOG Descriptor (feature vector),
* a zoomed-in portion of the HOG Descriptor, and
* the histogram of the selected cell.

**You can click anywhere on the gray scale image or the HOG Descriptor image to select a particular cell**. Once you click on either image a *magenta* rectangle will appear showing the cell you selected. The Zoom Window will show you a zoomed in version of the HOG descriptor around the selected cell; and the histogram plot will show you the corresponding histogram for the selected cell. The interactive window also has buttons at the bottom that allow for other functionality, such as panning, and giving you the option to save the figure if desired. The home button returns the figure to its default value.

**NOTE**: If you are running this notebook in the Udacity workspace, there is around a 2 second lag in the interactive plot. This means that if you click in the image to zoom in, it will take about 2 seconds for the plot to refresh.

```python
%matplotlib notebook

import copy
import matplotlib.patches as patches

# Set the default figure size
plt.rcParams['figure.figsize'] = [9.8, 9]

# Reshape the feature vector to [blocks_y, blocks_x, num_cells_per_block_x, num_cells_per_block_y, num_bins].
# The blocks_x and blocks_y will be transposed so that the first index (blocks_y) referes to the row number
# and the second index to the column number. This will be useful later when we plot the feature vector, so
# that the feature vector indexing matches the image indexing.
hog_descriptor_reshaped = hog_descriptor.reshape(tot_bx,
                                                 tot_by,
                                                 num_cells_per_block[0],
                                                 num_cells_per_block[1],
                                                 num_bins).transpose((1, 0, 2, 3, 4))

# Print the shape of the feature vector for reference
print('The feature vector has shape:', hog_descriptor.shape)

# Print the reshaped feature vector
print('The reshaped feature vector has shape:', hog_descriptor_reshaped.shape)

# Create an array that will hold the average gradients for each cell
ave_grad = np.zeros((y_cells, x_cells, num_bins))

# Print the shape of the ave_grad array for reference
print('The average gradient array has shape: ', ave_grad.shape)

# Create an array that will count the number of histograms per cell
hist_counter = np.zeros((y_cells, x_cells, 1))

# Add up all the histograms for each cell and count the number of histograms per cell
for i in range (num_cells_per_block[0]):
    for j in range(num_cells_per_block[1]):
        ave_grad[i:tot_by + i,
                 j:tot_bx + j] += hog_descriptor_reshaped[:, :, i, j, :]

        hist_counter[i:tot_by + i,
                     j:tot_bx + j] += 1

# Calculate the average gradient for each cell
ave_grad /= hist_counter

# Calculate the total number of vectors we have in all the cells.
len_vecs = ave_grad.shape[0] * ave_grad.shape[1] * ave_grad.shape[2]

# Create an array that has num_bins equally spaced between 0 and 180 degress in radians.
deg = np.linspace(0, np.pi, num_bins, endpoint = False)

# Each cell will have a histogram with num_bins. For each cell, plot each bin as a vector (with its magnitude
# equal to the height of the bin in the histogram, and its angle corresponding to the bin in the histogram).
# To do this, create rank 1 arrays that will hold the (x,y)-coordinate of all the vectors in all the cells in the
# image. Also, create the rank 1 arrays that will hold all the (U,V)-components of all the vectors in all the
# cells in the image. Create the arrays that will hold all the vector positons and components.
U = np.zeros((len_vecs))
V = np.zeros((len_vecs))
X = np.zeros((len_vecs))
Y = np.zeros((len_vecs))

# Set the counter to zero
counter = 0

# Use the cosine and sine functions to calculate the vector components (U,V) from their maginitudes. Remember the
# cosine and sine functions take angles in radians. Calculate the vector positions and magnitudes from the
# average gradient array
for i in range(ave_grad.shape[0]):
    for j in range(ave_grad.shape[1]):
        for k in range(ave_grad.shape[2]):
            U[counter] = ave_grad[i,j,k] * np.cos(deg[k])
            V[counter] = ave_grad[i,j,k] * np.sin(deg[k])

            X[counter] = (cell_size[0] / 2) + (cell_size[0] * i)
            Y[counter] = (cell_size[1] / 2) + (cell_size[1] * j)

            counter = counter + 1

# Create the bins in degress to plot our histogram.
angle_axis = np.linspace(0, 180, num_bins, endpoint = False)
angle_axis += ((angle_axis[1] - angle_axis[0]) / 2)

# Create a figure with 4 subplots arranged in 2 x 2
fig, ((a,b),(c,d)) = plt.subplots(2,2)

# Set the title of each subplot
a.set(title = 'Gray Scale Image\n(Click to Zoom)')
b.set(title = 'HOG Descriptor\n(Click to Zoom)')
c.set(title = 'Zoom Window', xlim = (0, 18), ylim = (0, 18), autoscale_on = False)
d.set(title = 'Histogram of Gradients')

# Plot the gray scale image
a.imshow(gray_image, cmap = 'gray')
a.set_aspect(aspect = 1)

# Plot the feature vector (HOG Descriptor)
b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
b.invert_yaxis()
b.set_aspect(aspect = 1)
b.set_facecolor('black')

# Define function for interactive zoom
def onpress(event):

    #Unless the left mouse button is pressed do nothing
    if event.button != 1:
        return

    # Only accept clicks for subplots a and b
    if event.inaxes in [a, b]:

        # Get mouse click coordinates
        x, y = event.xdata, event.ydata

        # Select the cell closest to the mouse click coordinates
        cell_num_x = np.uint32(x / cell_size[0])
        cell_num_y = np.uint32(y / cell_size[1])

        # Set the edge coordinates of the rectangle patch
        edgex = x - (x % cell_size[0])
        edgey = y - (y % cell_size[1])

        # Create a rectangle patch that matches the the cell selected above
        rect = patches.Rectangle((edgex, edgey),
                                  cell_size[0], cell_size[1],
                                  linewidth = 1,
                                  edgecolor = 'magenta',
                                  facecolor='none')

        # A single patch can only be used in a single plot. Create copies
        # of the patch to use in the other subplots
        rect2 = copy.copy(rect)
        rect3 = copy.copy(rect)

        # Update all subplots
        a.clear()
        a.set(title = 'Gray Scale Image\n(Click to Zoom)')
        a.imshow(gray_image, cmap = 'gray')
        a.set_aspect(aspect = 1)
        a.add_patch(rect)

        b.clear()
        b.set(title = 'HOG Descriptor\n(Click to Zoom)')
        b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
        b.invert_yaxis()
        b.set_aspect(aspect = 1)
        b.set_facecolor('black')
        b.add_patch(rect2)

        c.clear()
        c.set(title = 'Zoom Window')
        c.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 1)
        c.set_xlim(edgex - cell_size[0], edgex + (2 * cell_size[0]))
        c.set_ylim(edgey - cell_size[1], edgey + (2 * cell_size[1]))
        c.invert_yaxis()
        c.set_aspect(aspect = 1)
        c.set_facecolor('black')
        c.add_patch(rect3)

        d.clear()
        d.set(title = 'Histogram of Gradients')
        d.grid()
        d.set_xlim(0, 180)
        d.set_xticks(angle_axis)
        d.set_xlabel('Angle')
        d.bar(angle_axis,
              ave_grad[cell_num_y, cell_num_x, :],
              180 // num_bins,
              align = 'center',
              alpha = 0.5,
              linewidth = 1.2,
              edgecolor = 'k')

        fig.canvas.draw()

# Create a connection between the figure and the mouse click
fig.canvas.mpl_connect('button_press_event', onpress)
plt.show()
```

    The feature vector has shape: (57600, 1)
    The reshaped feature vector has shape: (40, 40, 2, 2, 9)
    The average gradient array has shape:  (41, 41, 9)

    <IPython.core.display.Javascript object>

# Understanding The Histograms

Let's take a look at a couple of snapshots of the above figure to see if the histograms for the selected cell make sense. Let's start looking at a cell that is inside a triangle and not near an edge:

Fig. 4. - Histograms Inside a Triangle.

In this case, since the triangle is nearly all of the same color there shouldn't be any dominant gradient in the selected cell. As we can clearly see in the Zoom Window and the histogram, this is indeed the case. We have many gradients but none of them clearly dominates over the other.

Now let’s take a look at a cell that is near a horizontal edge:

Fig. 5. - Histograms Near a Horizontal Edge.

Remember that edges are areas of an image where the intensity changes abruptly. In these cases, we will have a high intensity gradient in some particular direction. This is exactly what we see in the corresponding histogram and Zoom Window for the selected cell.  In the Zoom Window, we can see that the dominant gradient is pointing up, almost at 90 degrees, since that’s the direction in which there is a sharp change in intensity. Therefore, we should expect to see the 90-degree bin in the histogram to dominate strongly over the others. This is in fact what we see.

Now let’s take a look at a cell that is near a vertical edge:

Fig. 6. - Histograms Near a Vertical Edge.

In this case we expect the dominant gradient in the cell to be horizontal, close to 180 degrees, since that’s the direction in which there is a sharp change in intensity. Therefore, we should expect to see the 170-degree bin in the histogram to dominate strongly over the others. This is what we see in the histogram but we also see that there is another dominant gradient in the cell, namely the one in the 10-degree bin. The reason for this, is because the HOG algorithm is using unsigned gradients, which means 0 degrees and 180 degrees are considered the same. Therefore, when the histograms are being created, angles between 160 and 180 degrees, contribute proportionally to both the 10-degree bin and the 170-degree bin. This results in there being two dominant gradients in the cell near the vertical edge instead of just one.

To conclude let’s take a look at a cell that is near a diagonal edge.

Fig. 7. - Histograms Near a Diagonal Edge.

To understand what we are seeing, let’s first remember that gradients have an *x*-component, and a *y*-component, just like vectors. Therefore, the resulting orientation of a gradient is going to be given by the vector sum of its components. For this reason, on vertical edges the gradients are horizontal, because they only have an x-component, as we saw in Figure 4. While on horizontal edges the gradients are vertical, because they only have a y-component, as we saw in Figure 3. Consequently, on diagonal edges, the gradients are also going to be diagonal because both the *x* and *y* components are non-zero.  Since the diagonal edges in the image are close to 45 degrees, we should expect to see a dominant gradient orientation in the 50-degree bin. This is in fact what we see in the histogram but, just like in Figure 4., we see there are two dominant gradients instead of just one. The reason for this is that when the histograms are being created, angles that are near the boundaries of bins, contribute proportionally to the adjacent bins.  For example, a gradient with an angle of 40 degrees, is right in the middle of the 30-degree and 50-degree bin. Therefore, the magnitude of the gradient is split evenly into the 30-degree and 50-degree bin. This results in there being two dominant gradients in the cell near the diagonal edge instead of just one.

Now that you know how HOG is implemented, in the workspace you will find a notebook named *Examples*. In there, you will be able set your own paramters for the HOG descriptor for various images. Have fun!


# メモ
チャート式基礎からの数学Ⅲ 3章 微分法

を読む

微分係数

定義 $\quad$ 関数 $f(x)$ の $x-a$ における微分係数

$\qquad f'(a)= \displaystyle \lim_{h \to 0} \frac {f(a+h)-f(a)}{h} = \lim_{x \to a} \frac {f(x)-f(a)}{x-a}$

微分可能と連続 $\quad$ $f(x)$ が $x-a$ において微分可能ならば、$f(x)$ は $x-a$ において連続である。ただし、逆は成り立たない。

* 命題 proposition $\qquad$ $p \rightarrow q$
* 逆 converse $\qquad$ $q \rightarrow p$
* 裏 reverse  $\qquad$ $\neg p \rightarrow \neg q$
* 対偶 contraposition  $\qquad$ $\neg q \rightarrow \neg p$

導関数

定義 $\quad$ 関数 $f(x)$ の 導関数

$\qquad f'(a)= \displaystyle \lim_{h \to 0} \frac {f(x+h)-f(x)}{h}$

---
基本例題 46 p.81 (1)

定義に従って、関数 $y = \sqrt x$ の $x=1$ における微分係数を求める。

$\displaystyle \frac {\sqrt{1+h} - 1}{h} = \frac {(\sqrt{1+h} - 1)(\sqrt{1+h} + 1)}{h(\sqrt{1+h} + 1)}$

$\qquad = \displaystyle \frac {h}{h(\sqrt{1+h} + 1)}$

$\qquad = \displaystyle \frac {1}{\sqrt{1+h} + 1}$

$\qquad = \displaystyle \frac 1 2$

基本例題 46 (2) p.81

関数 $y = \;\mid x-a\;\mid\;$ について、$x=a$ における連続性、微分可能性を調べる。

解:

$f(x) = \;\mid x-a\;\mid\;$ と置くと、

$\qquad \displaystyle \lim_{x \to a} f(x) = \lim_{x \to a} \mid x-a\mid = 0 = f(a)$

よって、$f(a)$ が極限 $\displaystyle \lim_{x \to a} f(x)$と同じなので連続である。

次に $h \to 0$ と $h \to -0$ での微分係数を調べると極限値が $\pm 1$ となり異なるので微分可能ではない。

---
導関数の性質

$k,l$ を定数とする。
* 定数倍 $\quad$ $(ku)' = ku'$
* 和 $\quad (u+v)' = u' + v'$
* $(ku+lv)' = ku' +lv' \quad$ 特に $\quad (u-v)'=u'-v'$

積の導関数
* $(uv)' = u'v + uv'$
* $n$ が自然数の時 $\quad (x^n)' = nx^{n-1}$

---
商の導関数 p.83

* $\displaystyle \left(\frac u v\right)'= \frac{u'v - uv'}{v^2}\quad$ 特に $\quad \displaystyle \left(\frac 1 v \right) = - \frac {v'}{v^2}$

$n$ が整数の時、$\quad (x^n)' = nx^{n-1}$

$n$ が自然数の時は二項定理もしくは帰納法で証明する。

$n$ が負の時、$n=-m$ と置いて、

$(x^n)' = (x^{-m})' = \left(\frac 1 {x^m}\right)'= -\frac{(x^m)'}{(x^m)^2} = - \frac {mx^{m-1}}{x^{2m}} = -mx^{-m-1} = nx^{n-1}$

---
合成関数の導関数

$y=f(u)$ が $u$ の関数として微分可能、$u=g(x)$ が $x$ の関数として微分可能である時、関数 $y=f(u),\;u=g(x)$ の合成関数 $y=f(g(x))$ も $x$ の関数として微分可能である。

$\qquad \displaystyle \frac {dy}{dx} = \frac {dy}{du} \cdot \frac {du}{dx}$

一般に

$\qquad \{f(g(x))\}'=f'(g(x))\dot g'(x),\;\displaystyle\frac{d}{dx}f(y)=f'(y)\cdot\frac{dy}{dx}$

逆関数の微分法

微分可能な関数 $y=f(x)$ の逆関数 $f^{-1}(x)$ が存在する時、

$\qquad \displaystyle \frac {dy}{dx} = \frac {1}{\displaystyle \frac {dx}{dy}}$

証明

微分可能な関数 $y=f(x)$ の逆関数 $f^{-1}(x)$ が存在する時、$y=f(x)$ とすると、$x=f(y)$、これを左右 $x$ について微分すると、

左辺は $\quad\displaystyle \frac {d}{dx}x=1$

右辺は $\quad\displaystyle\frac{d}{dx}f(y)=\frac{d}{dx}f(y)\cdot\frac{dy}{dx}=\frac{dx}{dy}\cdot\frac{dy}{dx}$

よって、

$\qquad 1 = \displaystyle\frac{dx}{dy}\cdot\frac{dy}{dx}$

$\qquad \displaystyle\frac{dy}{dx}=\frac{1}{\displaystyle\frac{dx}{dy}}$

---
基本例題 47 (1) p.84

$u,v,w$ が $x$ の関数で微分可能であるとき、次の公式を証明する。

$\qquad (uvw)' = u'vw+uv'w+uvw'$

証明

$(uv)'=u'v+uv'$ より、

$\qquad (uvw)' = ((uv)w)' = (uv)'w + uvw' = u'vw + uv'w + uvw'$

---
基本例題 47 (2) p.84

次の関数を微分する。

```python
from sympy.abc import *
from sympy import *
y = x**4 + 2*x**3 -3*x
display(y)
display(diff(y))
```

$\displaystyle x^{4} + 2 x^{3} - 3 x$

$\displaystyle 4 x^{3} + 6 x^{2} - 3$

```python
from sympy.abc import *
from sympy import *
y = (x**2-2*x)*(4*x**2+3*x)
display(y)
display(diff(y))
display(expand(diff(y)))
```

$\displaystyle \left(x^{2} - 2 x\right) \left(4 x^{2} + 3 x\right)$

$\displaystyle \left(2 x - 2\right) \left(4 x^{2} + 3 x\right) + \left(8 x + 3\right) \left(x^{2} - 2 x\right)$

$\displaystyle 16 x^{3} - 15 x^{2} - 12 x$

```python
from sympy.abc import *
from sympy import *
y = (2*x**3+x-1)/x**2
display(y)
display(apart(y))
display(expand(diff(y))) # 商の導関数の公式を使う (1/v)' = v'/v**2
```

$\displaystyle \frac{2 x^{3} + x - 1}{x^{2}}$

$\displaystyle 2 x + \frac{1}{x} - \frac{1}{x^{2}}$

$\displaystyle 2 - \frac{1}{x^{2}} + \frac{2}{x^{3}}$

```python
from sympy.abc import *
from sympy import *
y = (2*x-3)/(x**2+1)
display(y)
display(diff(y))
display(cancel(diff(y))) # 通分は cancel か simplify を使う
```

$\displaystyle \frac{2 x - 3}{x^{2} + 1}$

$\displaystyle - \frac{2 x \left(2 x - 3\right)}{\left(x^{2} + 1\right)^{2}} + \frac{2}{x^{2} + 1}$

$\displaystyle \frac{- 2 x^{2} + 6 x + 2}{x^{4} + 2 x^{2} + 1}$

---
基本例題 48 p.85

次の関数を微分する。

```python
from sympy.abc import *
from sympy import *
y = (x**2+1)**3
display(y)
display(diff(y)) # 合成関数の微分
```

$\displaystyle \left(x^{2} + 1\right)^{3}$

$\displaystyle 6 x \left(x^{2} + 1\right)^{2}$

```python
from sympy.abc import *
from sympy import *
y = 1 / (2*x-3)**2
display(y)
display(diff(y))
```

$\displaystyle \frac{1}{\left(2 x - 3\right)^{2}}$

$\displaystyle - \frac{4}{\left(2 x - 3\right)^{3}}$

```python
from sympy.abc import *
from sympy import *
y =(3*x+1)**2*(x-2)
display(y)
display(diff(y))
display(simplify(diff(y)))
```

$\displaystyle \left(x - 2\right) \left(3 x + 1\right)^{2}$

$\displaystyle \left(x - 2\right) \left(18 x + 6\right) + \left(3 x + 1\right)^{2}$

$\displaystyle \left(3 x + 1\right) \left(9 x - 11\right)$

```python
from sympy.abc import *
from sympy import *
y =(x-1)/(x**2+1)**2
display(y)
display(diff(y))
display(cancel(diff(y)))
```

$\displaystyle \frac{x - 1}{\left(x^{2} + 1\right)^{2}}$

$\displaystyle - \frac{4 x \left(x - 1\right)}{\left(x^{2} + 1\right)^{3}} + \frac{1}{\left(x^{2} + 1\right)^{2}}$

$\displaystyle \frac{- 3 x^{2} + 4 x + 1}{x^{6} + 3 x^{4} + 3 x^{2} + 1}$

---
基本例題 49 p.86 (1)

$(x^3)'=3x^2,\;\; \displaystyle\frac{dy}{dx} =\frac{1}{\displaystyle\frac{dx}{dy}}$ を用いて、$y=x^3$ の逆関数 $y=f(x)$ の導関数を求める。

解:

```python
from sympy.abc import *
from sympy import *
y = x**(Rational(1,3))
display(y)
print()
display(diff(y))
```

$\displaystyle \sqrt[3]{x}$

$\displaystyle \frac{1}{3 x^{\frac{2}{3}}}$

---
基本例題 49 p.86 (2)

$r$ が有理数の時、$(x^r)'=rx^{r-1}$ を証明せよ。

証明

$p$ が自然数 (正の整数) の時、$y= x^{\frac{1}{p}}$とすると、$x=y^p$

よって、$\displaystyle\frac{dy}{dx}=\frac{1}{\frac{dy}{dx}}= \frac{1}{py^{p-1}}=\frac{1}{p}y^{1-p}=\frac{1}{p}(x^{\frac{1}{p}})^{1-p}=\frac{1}{p}x^{(\frac{1}{p}-1)}$

$r$ が有理数の時、$r=\frac{q}{p},\; p$ は自然数 (正の整数)、$q$ は整数。

$\qquad \displaystyle (x^{\frac{q}{p}})'=  ((x^{\frac{1}{p}})^q)'
= q(x^{\frac{1}{p}})^{q-1}(x^{\frac{1}{p}})'$
$\qquad\qquad
= q(x^{\frac{1}{p}})^{q-1}\cdot\frac{1}{p}x^{\frac{1}{p}-1}
= \frac{q}{p}x^{\frac{q}{p}-\frac{1}{p}+\frac{1}{p}-1}$

ゆえに、$\qquad \displaystyle (x^{\frac{q}{p}})'=\frac{q}{p}x^{\frac{q}{p}-1}$

すなわち

$(x^r)'=rx^{r-1}$

証明終わり

```python
# 実験
from sympy.abc import *
from sympy import *
y = 1/ (x**2)**(Rational(1,3))
display(y)
print()
display(diff(y))
print()
x,y = symbols('x,y')
x = 1/sqrt(y**3)
display(x)
display(diff(x,y))

```

$\displaystyle \frac{1}{\sqrt[3]{x^{2}}}$

$\displaystyle - \frac{2}{3 x \sqrt[3]{x^{2}}}$

$\displaystyle \frac{1}{\sqrt{y^{3}}}$

$\displaystyle - \frac{3}{2 y \sqrt{y^{3}}}$


# Data prepare for regression

```python
%matplotlib inline
%load_ext autoreload
%autoreload 2
```

```python
from jupyterthemes import jtplot
jtplot.style(theme='onedork', context='notebook', ticks=True, grid=False)
```

```python
import pandas as pd
pd.options.display.max_rows = 999
pd.options.display.max_columns = 999
pd.set_option("display.max_columns", None)
import numpy as np
import os
import matplotlib.pyplot as plt
from pylab import rcParams
rcParams['figure.figsize'] = 15, 5

import data
import copy
from rolldecay.bis_system import BisSystem
from rolldecay import database
from rolldecayestimators import equations, symbols

from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import VarianceThreshold
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.feature_selection import f_regression

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_validate
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.pipeline import make_pipeline

import signal_lab
from sqlalchemy.inspection import inspect
import seaborn as sns
import docs
from sympy.physics.vector.printing import vpprint, vlatex
from IPython.display import display, Math, Latex
import sympy as sp
from rolldecayestimators.substitute_dynamic_symbols import lambdify,run
import rolldecayestimators.lambdas as lambdas
from rolldecay.froude_scaling import froude_scale
```

```python
df_rolldecay = database.load(rolldecay_table_name='rolldecay_quadratic_b', limit_score=0.90,
                             exclude_table_name='rolldecay_exclude')
df_rolldecay['ship_speed']*=1.852/3.6
df_rolldecay['VDES']*=1.852/3.6

#Assuming bilge keel None as zero!
df_rolldecay['BKB'] = df_rolldecay['BKB'].fillna(0)
df_rolldecay['BKL'] =df_rolldecay['BKL'].fillna(0)

T = (df_rolldecay['TF']+df_rolldecay['TA'])/2
lpp = df_rolldecay['lpp']
beam = df_rolldecay['beam']

df_rolldecay['CB'] = df_rolldecay['Volume']/(T*lpp*beam)

db = database.get_db()
```

```python
df_rolldecay.columns
```

```python
df_rolldecay.describe()
```

```python
df_rolldecay.isnull().sum().sort_values(ascending=False)
```

```python
description = pd.read_sql_table('description', con=db.engine, index_col='id')
description.loc['ship_speed','unit']='m/s'
description.loc['VDES','unit']='m/s'
```

```python
description
```

```python
interesting=[
'A0',
'AR',
'beam',
'BKB',
'BKL',
'BKX',
'BR',
'BRA',
'BTT1',
'CP',
'CB',
'CW',
'Volume',
'gm',
'HSKEG',
'ASKEG',
'IRUD',
'kg',
'KXX',
'lcg',
'lpp',
'LSKEG',
'RH',
'RHBL',
'RTYPE',
'TA',
'TF',
'TWIN',
'ship_type_id',
'ship_speed',
]
```

```python
description.loc[interesting]
```

```python
description.loc['phi_start'] = {'description':'roll at start of test','unit':'rad'}
description.loc['phi_stop'] = {'description':'roll at end of test','unit':'rad'}
description.loc['Disp'] = {'description':'Ship discplacement','unit':'m3'}

skip = [
    'model_number',
    'loading_condition_id',
    'B_1A',
    'B_2A',
    'B_3A',
    'C_1A',
    'C_3A',
    'C_5A',
    'B_1A',
    'B_1',
    'B_2',
    'B_3',
    'C_1',
    'C_3',
    'C_5',
    'A_44',
    'omega0_fft',
    'omega0',
    'score',
    'id',
    'project_number',
    'series_number',
    'run_number',
    'test_number',
    'scale_factor',
    'g',
    'rho',
    'B_1_hat',
    'B_2_hat',
    'CB',
]
```

```python
df_model = froude_scale(data=df_rolldecay, description=description, skip=skip)
```

```python
df_model.describe()
```

```python
mask = df_model[interesting].notnull().all(axis=1)
mask.sum()
```

```python
df_model[interesting].isnull().sum()
```

```python
remove = ['BTT1','BR','BRA','HSKEG','ASKEG','LSKEG','RHBL','RTYPE','lcg','BKX',]
add = ['ship_speed',]
important = list(set(interesting) - set(remove)) + add

```

```python
mask = df_model[important].notnull().all(axis=1)
mask.sum()
```

```python
df_model[important].isnull().sum()
```

```python
df = df_model.loc[mask].copy()
```

```python
df = df.dropna(subset=['omega0'])
```

```python
len(df)
```

```python
mask = df['score'] > 0.95
df = df.loc[mask]
```

```python
important
```

```python

```

## Bis system

```python
#data = df[list(set(important) | set(['omega0','B_1','B_2',]))].copy()
#units = description.loc[data.columns]['unit']
#data['ship_speed']*=1.852/3.6
#units['ship_speed']=r'm/s'
#
#data['g']=9.81
#data['rho']=1000
#units['g']=r'm/s**2'
#units['rho']=r'kg/m**3'
#
#data['omega0_hat'] = data['omega0']*np.sqrt(data['beam']/(2*data['g']))
#units['omega0_hat'] = '-'
#
#bis_system = BisSystem(lpp=data['lpp'], volume=data['Volume'], units=units)
#data = bis_system.df_to_bis(data)
#
```

```python
data = df[list(set(important))].copy()
```

```python
df.hist('score', bins = 50)
```

```python
df.hist('omega0', bins = 50)
```

```python
Math(vlatex(equations.B44_hat_equation))
```

```python
equations.B44_hat_equation_quadratic
```

```python
lambdas.B44_lambda
```

```python
equations.B_1_hat_equation
```

```python
equations.B_2_hat_equation
```

```python
df['g']=9.81
df['rho']=1000
df['Disp']=df['Volume']
df['B_1_hat'] = run(function=lambdas.B_1_hat_lambda, inputs=df)
df['B_2_hat'] = run(function=lambdas.B_2_hat_lambda, inputs=df)
```

```python
equations.omega0_hat_equation
```

```python
df['omega0_hat']=run(function=lambdas.omega0_lambda,inputs=df)
```

```python
df.hist('omega0_hat', bins = 50)
```

```python
df.hist('B_1_hat', bins = 50)
```

```python
df.hist('B_2_hat', bins = 50)
```

```python
X=data
```

```python
variance_treshold = VarianceThreshold(0.001)
X_ = variance_treshold.fit_transform(X)
```

```python
X.shape
```

```python
X_.shape
```

```python
X_ = variance_treshold.fit_transform(X)
X_.shape
```

```python
X.columns[variance_treshold.get_support()]
```

```python
#GM = data['gm']
#rxx = data['KXX']
#kxx = rxx/data['beam']
#rho = data['rho']
#m = rho*data['Volume']
#Ixx = m*rxx**2
#data['Ixx']=Ixx
#if not 'Ixx' in important:
#    important.append('Ixx')
#g = data['g']
#omega0 = data['omega0']
#data['Ixx_tot'] = Ixx_tot = GM*g*m/(omega0**2)
#data['Ixx_added'] = Ixx_added = Ixx_tot - Ixx
```

```python
#mask = data['Ixx_added']>0
#data = data.loc[mask].copy()
```

```python
important = list(set(important))  # Only unique values
X=data[important]
```

```python
renamers = {
    'CP' : 'C_p',
    'CB' : 'C_b',
    'IRUD' : 'I_RUD',
    'BKL' : 'BK_L',
    'gm' : 'GM',
    'A0' : 'A_0',
    'ship_type_id' : 'ship_type_id',
    'Volume' : 'Disp',
    'Ixx' : 'I_xx',
    'BKB' : 'BK_B',
    'KXX' : 'K_xx',
    'RH' : 'R_h',
    'AR' : 'A_R',
    'TWIN' : 'TWIN',
    'kg': 'kg',
    'CW' : 'C_W',
    'beam' : 'beam',
    'TF' : 'T_F',
    'ship_speed' : 'V',
    'TA' : 'T_A',
    'lpp' : 'L_pp',
}
X.rename(columns=renamers, inplace=True)
```

```python
X['T'] = (X['T_A'] + X['T_F'])/2
X.drop(columns=['T_A','T_F'], inplace=True)
```

```python
X.head()
```

```python
X.drop(columns='ship_type_id', inplace=True)
```

```python
X.to_pickle('data.sav')
```

```python
y_s = ['omega0_hat','B_1_hat','B_2_hat']
y = df[y_s].copy()
y.to_pickle('y.sav')
```

```python
X.describe()
```

```python
df_rolldecay.describe()
```

```python
loss_ratio = len(X)/len(df_rolldecay)
loss_ratio
```

```python

```