# Start-to-Finish Example: `GiRaFFE_NRPy` 3D tests

### Author: Patrick Nelson

### Adapted from [Start-to-Finish Example: Head-On Black Hole Collision](../Tutorial-Start_to_Finish-BSSNCurvilinear-Two_BHs_Collide.ipynb)

## This module implements a basic GRFFE code to evolve one-dimensional GRFFE waves.

### NRPy+ Source Code for this module:
* [GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Exact_Wald.py](../../edit/in_progress/GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Exact_Wald.py) [\[**tutorial**\]](Tutorial-GiRaFFEfood_NRPy_Exact_Wald.ipynb) Generates Exact Wald initial data
* [GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Aligned_Rotator.py](../../edit/in_progress/GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_Aligned_Rotator.py) [\[**tutorial**\]](Tutorial-GiRaFFEfood_NRPy_Aligned_Rotator.ipynb) Generates Aligned Rotator initial data
* [GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_1D_tests.py](../../edit/in_progress/GiRaFFEfood_NRPy/GiRaFFEfood_NRPy_1D_tests.py) [\[**tutorial**\]](Tutorial-GiRaFFEfood_NRPy_1D_tests.ipynb) Generates Alfvén Wave initial data.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_Afield_flux.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_Afield_flux.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy_staggered-Afield_flux.ipynb) Generates the expressions to find the flux term of the induction equation.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_A2B.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_A2B.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy_staggered-A2B.ipynb) Generates the driver to compute the magnetic field from the vector potential/
* [GiRaFFE_NRPy/GiRaFFE_NRPy_BCs.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_BCs.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-BCs.ipynb) Generates the code to apply boundary conditions to the vector potential, scalar potential, and three-velocity.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_C2P_P2C.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_C2P_P2C.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-C2P_P2C.ipynb) Generates the conservative-to-primitive and primitive-to-conservative solvers.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_Metric_Face_Values.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_Metric_Face_Values.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-Metric_Face_Values.ipynb) Generates code to interpolate metric gridfunctions to cell faces.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_PPM.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_PPM.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-PPM.ipynb) Genearates code to reconstruct primitive variables on cell faces.
* [GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_Source_Terms.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_staggered_Source_Terms.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy_staggered-Source_Terms.ipynb) Generates the expressions to find the flux term of the Poynting flux evolution equation.
* [GiRaFFE_NRPy/Stilde_flux.py](../../edit/in_progress/GiRaFFE_NRPy/Stilde_flux.py) [\[**tutorial**\]](Tutorial-GiRaFFE_NRPy-Stilde_flux.ipynb) Generates the expressions to find the flux term of the Poynting flux evolution equation.
* [../GRFFE/equations.py](../../edit/GRFFE/equations.py) [\[**tutorial**\]](../Tutorial-GRFFE_Equations-Cartesian.ipynb) Generates code necessary to compute the source terms.
* [../GRHD/equations.py](../../edit/GRHD/equations.py) [\[**tutorial**\]](../Tutorial-GRHD_Equations-Cartesian.ipynb) Generates code necessary to compute the source terms.

Here we use NRPy+ to generate the C source code necessary to set up initial data for an Exact Wald (see [the original GiRaFFE paper](https://arxiv.org/pdf/1704.00599.pdf)). Then we use it to generate the RHS expressions for [Method of Lines](https://reference.wolfram.com/language/tutorial/NDSolveMethodOfLines.html) time integration based on the [explicit Runge-Kutta fourth-order scheme](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods) (RK4).

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

This notebook is organized as follows

1. [Step 1](#initializenrpy): Set core NRPy+ parameters for numerical grids
1. [Step 2](#grffe): Output C code for GRFFE evolution
    1. [Step 2.a](#mol): Output macros for Method of Lines timestepping
1. [Step 3](#gf_id): Import `GiRaFFEfood_NRPy` initial data modules
1. [Step 4](#cparams): Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h`
1. [Step 5](#mainc): `GiRaFFE_NRPy_standalone.c`: The Main C Code

# Step 1: Set up core functions and parameters for solving  GRFFE equations \[Back to [top](#toc)\]
$$\label{setup}$$

```python
import shutil, os, sys           # Standard Python modules for multiplatform OS-level functions
# First, we'll add the parent directory to the list of directories Python will check for modules.
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Step P1: Import needed NRPy+ core modules:
from outputC import outCfunction, lhrh # NRPy+: Core C code output module
import sympy as sp               # SymPy: The Python computer algebra package upon which NRPy+ depends
import finite_difference as fin  # NRPy+: Finite difference C code generation module
import NRPy_param_funcs as par   # NRPy+: Parameter interface
import grid as gri               # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import cmdline_helper as cmd     # NRPy+: Multi-platform Python command-line interface

# Step P2: Create C code output directory:
Ccodesdir = os.path.join("GiRaFFE_staggered_standalone_Ccodes/")
# First remove C code output directory if it exists
# Courtesy https://stackoverflow.com/questions/303200/how-do-i-remove-delete-a-folder-that-is-not-empty
# !rm -r ScalarWaveCurvilinear_Playground_Ccodes
shutil.rmtree(Ccodesdir, ignore_errors=True)
# Then create a fresh directory
cmd.mkdir(Ccodesdir)

# Step P3: Create executable output directory:
outdir = os.path.join(Ccodesdir,"output/")
cmd.mkdir(outdir)

# Step P5: Set timestepping algorithm (we adopt the Method of Lines)
REAL      = "double"    # Best to use double here.
default_CFL_FACTOR= 0.5 # (GETS OVERWRITTEN WHEN EXECUTED.) In pure axisymmetry (symmetry_axes = 2 below) 1.0 works fine. Otherwise 0.5 or lower.

# Step P6: Set the finite differencing order to 2.
par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER",2)

thismodule = "Start_to_Finish-GiRaFFE_NRPy-1D_tests"
TINYDOUBLE = par.Cparameters("REAL", thismodule, "TINYDOUBLE", 1e-100)

import GiRaFFE_NRPy.GiRaFFE_NRPy_Main_Driver_staggered as md
# par.set_paramsvals_value("GiRaFFE_NRPy.GiRaFFE_NRPy_C2P_P2C::enforce_speed_limit_StildeD = False")
par.set_paramsvals_value("GiRaFFE_NRPy.GiRaFFE_NRPy_C2P_P2C::enforce_current_sheet_prescription = False")
```

# Step 2: Output C code for GRFFE evolution \[Back to [top](#toc)\]
$$\label{grffe}$$

We will first write the C codes needed for GRFFE evolution. We have already written a module to generate all these codes and call the functions in the appropriate order, so we will import that here. We will take the slightly unusual step of doing this before we generate the initial data functions because the main driver module will register all the gridfunctions we need. It will also generate functions that, in addition to their normal spot in the MoL timestepping, will need to be called during the initial data step to make sure all the variables are appropriately filled in.

All of this is handled with a single call to `GiRaFFE_NRPy_Main_Driver_generate_all()`, which will register gridfunctions, write all the C code kernels, and write the C code functions to call those.

```python
md.GiRaFFE_NRPy_Main_Driver_generate_all(Ccodesdir)
```

    Output C function calculate_StildeD0_source_term() to file GiRaFFE_staggered_standalone_Ccodes/RHSs/calculate_StildeD0_source_term.h
    Output C function calculate_StildeD1_source_term() to file GiRaFFE_staggered_standalone_Ccodes/RHSs/calculate_StildeD1_source_term.h
    Output C function calculate_StildeD2_source_term() to file GiRaFFE_staggered_standalone_Ccodes/RHSs/calculate_StildeD2_source_term.h
    Output C function calculate_Stilde_rhsD() to file GiRaFFE_staggered_standalone_Ccodes/RHSs/calculate_Stilde_rhsD.h
    Output C function GiRaFFE_NRPy_cons_to_prims() to file GiRaFFE_staggered_standalone_Ccodes/C2P/GiRaFFE_NRPy_cons_to_prims.h
    Output C function GiRaFFE_NRPy_prims_to_cons() to file GiRaFFE_staggered_standalone_Ccodes/C2P/GiRaFFE_NRPy_prims_to_cons.h
    Output C function Workaround_ADM_to_BSSN() to file GiRaFFE_staggered_standalone_Ccodes/Workaround_ADM_to_BSSN.h
    Output C function Workaround_BSSN_to_ADM() to file GiRaFFE_staggered_standalone_Ccodes/Workaround_BSSN_to_ADM.h

## Step 2.a: Output macros for Method of Lines timestepping \[Back to [top](#toc)\]
$$\label{mol}$$

Now, we generate the code to implement the method of lines using the fourth-order Runge-Kutta algorithm.

```python
RK_method = "RK4"

# Step 3: Generate Runge-Kutta-based (RK-based) timestepping code.
#       As described above the Table of Contents, this is a 3-step process:
#       3.A: Evaluate RHSs (RHS_string)
#       3.B: Apply boundary conditions (post_RHS_string, pt 1)
import MoLtimestepping.C_Code_Generation as MoL
from MoLtimestepping.RK_Butcher_Table_Dictionary import Butcher_dict
RK_order  = Butcher_dict[RK_method][1]
cmd.mkdir(os.path.join(Ccodesdir,"MoLtimestepping/"))
MoL.MoL_C_Code_Generation(RK_method,
    RHS_string      = """
GiRaFFE_NRPy_RHSs(&params,auxevol_gfs,RK_INPUT_GFS,RK_OUTPUT_GFS);""",
    post_RHS_string = """
GiRaFFE_NRPy_post_step(&params,xx,auxevol_gfs,RK_OUTPUT_GFS,n+1);\n""",
    outdir = os.path.join(Ccodesdir,"MoLtimestepping/"))
```

# Step 3: Import `GiRaFFEfood_NRPy` initial data modules \[Back to [top](#toc)\]
$$\label{gf_id}$$

With the preliminaries out of the way, we will write the C functions to set up initial data. There are two categories of initial data that must be set: the spacetime metric variables, and the GRFFE plasma variables. We will set up the spacetime first.

```python
# There are several initial data routines we need to test. We'll control which one we use with a string option
initial_data = "ExactWald" # Valid options: "ExactWald", "AlignedRotator"
spacetime = "ShiftedKerrSchild" # Valid options: "ShiftedKerrSchild", "flat"

if spacetime == "ShiftedKerrSchild":
    # Exact Wald is more complicated. We'll need the Shifted Kerr Schild metric in Cartesian coordinates.
    import BSSN.ShiftedKerrSchild as sks
    sks.ShiftedKerrSchild(True)
    import reference_metric as rfm
    par.set_parval_from_str("reference_metric::CoordSystem","Cartesian")
    rfm.reference_metric()
    # Use the Jacobian matrix to transform the vectors to Cartesian coordinates.
    drrefmetric__dx_0UDmatrix = sp.Matrix([[sp.diff(rfm.xxSph[0],rfm.xx[0]), sp.diff(rfm.xxSph[0],rfm.xx[1]), sp.diff(rfm.xxSph[0],rfm.xx[2])],
                                           [sp.diff(rfm.xxSph[1],rfm.xx[0]), sp.diff(rfm.xxSph[1],rfm.xx[1]), sp.diff(rfm.xxSph[1],rfm.xx[2])],
                                           [sp.diff(rfm.xxSph[2],rfm.xx[0]), sp.diff(rfm.xxSph[2],rfm.xx[1]), sp.diff(rfm.xxSph[2],rfm.xx[2])]])
    dx__drrefmetric_0UDmatrix = drrefmetric__dx_0UDmatrix.inv()
    gammaDD = ixp.zerorank2()
    for i in range(3):
        for j in range(3):
            for k in range(3):
                for l in range(3):
                    gammaDD[i][j] += drrefmetric__dx_0UDmatrix[(k,i)]*drrefmetric__dx_0UDmatrix[(l,j)]*sks.gammaSphDD[k][l].subs(sks.r,rfm.xxSph[0]).subs(sks.th,rfm.xxSph[1])

    betaU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            betaU[i] += dx__drrefmetric_0UDmatrix[(i,j)]*sks.betaSphU[j].subs(sks.r,rfm.xxSph[0]).subs(sks.th,rfm.xxSph[1])
    alpha = sks.alphaSph.subs(sks.r,rfm.xxSph[0]).subs(sks.th,rfm.xxSph[1])

    # Description and options for this initial data
    desc = "Generate a spinning black hole with Shifted Kerr Schild metric."
    loopopts_id ="AllPoints,Read_xxs"

elif spacetime == "flat":
    gammaDD = ixp.zerorank2(DIM=3)
    for i in range(3):
        for j in range(3):
            if i==j:
                gammaDD[i][j] = sp.sympify(1) # else: leave as zero
    betaU = ixp.zerorank1() # All should be 0
    alpha = sp.sympify(1)

    # Description and options for this initial data
    desc = "Generate a flat spacetime metric."
    loopopts_id ="AllPoints" # we don't need to read coordinates for flat spacetime.

# For testing: Also set inverse metric:
gammaUU, unused_gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)

name = "set_initial_spacetime_metric_data"
values_to_print = [
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD00"),rhs=gammaDD[0][0]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD01"),rhs=gammaDD[0][1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD02"),rhs=gammaDD[0][2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD11"),rhs=gammaDD[1][1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD12"),rhs=gammaDD[1][2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaDD22"),rhs=gammaDD[2][2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","betaU0"),rhs=betaU[0]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","betaU1"),rhs=betaU[1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","betaU2"),rhs=betaU[2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","alpha"),rhs=alpha),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU00"),rhs=gammaUU[0][0]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU01"),rhs=gammaUU[0][1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU02"),rhs=gammaUU[0][2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU11"),rhs=gammaUU[1][1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU12"),rhs=gammaUU[1][2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","gammaUU22"),rhs=gammaUU[2][2])
                  ]

outCfunction(
    outfile  = os.path.join(Ccodesdir,name+".h"), desc=desc, name=name,
    params   ="const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs",
    body     = fin.FD_outputC("returnstring",values_to_print,params="outCverbose=False").replace("IDX4","IDX4S"),
    loopopts = loopopts_id)

```

    Output C function set_initial_spacetime_metric_data() to file GiRaFFE_staggered_standalone_Ccodes/set_initial_spacetime_metric_data.h

Now, we will write out the initial data function for the GRFFE variables.

```python
if initial_data=="AlignedRotator":
    import GiRaFFEfood_NRPy.GiRaFFEfood_NRPy_Aligned_Rotator as gid
    gid.GiRaFFEfood_NRPy_Aligned_Rotator()
    desc = "Generate aligned rotator initial test data for GiRaFFEfood_NRPy."
elif initial_data=="ExactWald":
    import GiRaFFEfood_NRPy.GiRaFFEfood_NRPy_Exact_Wald as gid
    gid.GiRaFFEfood_NRPy_Exact_Wald(gammaDD,sks.M,sks.r0,stagger=True)
    desc = "Generate exact Wald initial test data for GiRaFFEfood_NRPy."
else:
    print("Unsupported Initial Data string "+initial_data+"! Supported ID: AlfvenWave, FastWave, AlignedRotator, or ExactWald")

name = "initial_data"

values_to_print = [
                   lhrh(lhs=gri.gfaccess("out_gfs","AD0"),rhs=gid.AD[0]),
                   lhrh(lhs=gri.gfaccess("out_gfs","AD1"),rhs=gid.AD[1]),
                   lhrh(lhs=gri.gfaccess("out_gfs","AD2"),rhs=gid.AD[2]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU0"),rhs=gid.ValenciavU[0]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU1"),rhs=gid.ValenciavU[1]),
                   lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU2"),rhs=gid.ValenciavU[2]),
#                    lhrh(lhs=gri.gfaccess("auxevol_gfs","BU0"),rhs=gid.BU[0]),
#                    lhrh(lhs=gri.gfaccess("auxevol_gfs","BU1"),rhs=gid.BU[1]),
#                    lhrh(lhs=gri.gfaccess("auxevol_gfs","BU2"),rhs=gid.BU[2]),
                   lhrh(lhs=gri.gfaccess("out_gfs","psi6Phi"),rhs=sp.sympify(0))
                  ]

outCfunction(
    outfile  = os.path.join(Ccodesdir,name+".h"), desc=desc, name=name,
    params   ="const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *out_gfs",
    body     = fin.FD_outputC("returnstring",values_to_print,params="outCverbose=False").replace("IDX4","IDX4S"),
    loopopts ="AllPoints,Read_xxs")
```

    Output C function initial_data() to file GiRaFFE_staggered_standalone_Ccodes/initial_data.h

# Step 4: Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h` \[Back to [top](#toc)\]
$$\label{cparams}$$

Based on declared NRPy+ Cparameters, first we generate `declare_Cparameters_struct.h`, `set_Cparameters_default.h`, and `set_Cparameters[-SIMD].h`.

Then we output `free_parameters.h`, which sets initial data parameters, as well as grid domain & reference metric parameters, applying `domain_size` and `sinh_width`/`SymTP_bScale` (if applicable) as set above

```python
# Step 3.e: Output C codes needed for declaring and setting Cparameters; also set free_parameters.h
# Step 3.e.i: Generate declare_Cparameters_struct.h, set_Cparameters_default.h, and set_Cparameters[-SIMD].h
par.generate_Cparameters_Ccodes(os.path.join(Ccodesdir))

# Step 3.e.ii: Set free_parameters.h
with open(os.path.join(Ccodesdir,"free_parameters.h"),"w") as file:
    file.write("""// Override parameter defaults with values based on command line arguments and NGHOSTS.
params.Nxx0 = atoi(argv[1]);
params.Nxx1 = atoi(argv[2]);
params.Nxx2 = atoi(argv[3]);
params.Nxx_plus_2NGHOSTS0 = params.Nxx0 + 2*NGHOSTS;
params.Nxx_plus_2NGHOSTS1 = params.Nxx1 + 2*NGHOSTS;
params.Nxx_plus_2NGHOSTS2 = params.Nxx2 + 2*NGHOSTS;
// Step 0d: Set up space and time coordinates
// Step 0d.i: Declare \Delta x^i=dxx{0,1,2} and invdxx{0,1,2}, as well as xxmin[3] and xxmax[3]:
const REAL xxmin[3] = {-1.5,-1.5,-1.5};
const REAL xxmax[3] = { 1.5, 1.5, 1.5};

params.dxx0 = (xxmax[0] - xxmin[0]) / ((REAL)params.Nxx0+1);
params.dxx1 = (xxmax[1] - xxmin[1]) / ((REAL)params.Nxx1+1);
params.dxx2 = (xxmax[2] - xxmin[2]) / ((REAL)params.Nxx2+1);
printf("dxx0,dxx1,dxx2 = %.15e,%.15e,%.15e\\n",params.dxx0,params.dxx1,params.dxx2);
params.invdx0 = 1.0 / params.dxx0;
params.invdx1 = 1.0 / params.dxx1;
params.invdx2 = 1.0 / params.dxx2;

const int poison_grids = 1;
// Standard GRFFE parameters:
params.GAMMA_SPEED_LIMIT = 2000.0;
params.diss_strength = 4.0;
""")
if initial_data=="ExactWald":
    with open(os.path.join(Ccodesdir,"free_parameters.h"),"a") as file:
        file.write("""params.r0 = 0.4;
params.a = 0.0;
""")

```

# Step 4: Set up boundary condition functions for chosen singular, curvilinear coordinate system \[Back to [top](#toc)\]
$$\label{bc_functs}$$

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

...But, for the moment, we're actually just using this because it writes the file `gridfunction_defines.h`.

```python
import CurviBoundaryConditions.CurviBoundaryConditions as cbcs
cbcs.Set_up_CurviBoundaryConditions(os.path.join(Ccodesdir,"boundary_conditions/"),Cparamspath=os.path.join("../"),enable_copy_of_static_Ccodes=False)
```

    Wrote to file "GiRaFFE_staggered_standalone_Ccodes/boundary_conditions/parity_conditions_symbolic_dot_products.h"
    Evolved parity: ( AD0:1, AD1:2, AD2:3, StildeD0:1, StildeD1:2, StildeD2:3,
        psi6Phi:0 )

    AuxEvol parity: ( BU0:1, BU1:2, BU2:3, B_lU0:1, B_lU1:2, B_lU2:3, B_rU0:1,
        B_rU1:2, B_rU2:3, BstaggerU0:1, BstaggerU1:2, BstaggerU2:3,
        Bstagger_lU0:1, Bstagger_lU1:2, Bstagger_lU2:3, Bstagger_rU0:1,
        Bstagger_rU1:2, Bstagger_rU2:3, Stilde_flux_HLLED0:1,
        Stilde_flux_HLLED1:2, Stilde_flux_HLLED2:3, ValenciavU0:1,
        ValenciavU1:2, ValenciavU2:3, Valenciav_lU0:1, Valenciav_lU1:2,
        Valenciav_lU2:3, Valenciav_llU0:1, Valenciav_llU1:2, Valenciav_llU2:3,
        Valenciav_lrU0:1, Valenciav_lrU1:2, Valenciav_lrU2:3, Valenciav_rU0:1,
        Valenciav_rU1:2, Valenciav_rU2:3, Valenciav_rlU0:1, Valenciav_rlU1:2,
        Valenciav_rlU2:3, Valenciav_rrU0:1, Valenciav_rrU1:2, Valenciav_rrU2:3,
        alpha:0, alpha_face:0, betaU0:1, betaU1:2, betaU2:3, beta_faceU0:1,
        beta_faceU1:2, beta_faceU2:3, cmax_x:0, cmax_y:0, cmax_z:0, cmin_x:0,
        cmin_y:0, cmin_z:0, gammaDD00:4, gammaDD01:5, gammaDD02:6, gammaDD11:7,
        gammaDD12:8, gammaDD22:9, gammaUU00:4, gammaUU01:5, gammaUU02:6,
        gammaUU11:7, gammaUU12:8, gammaUU22:9, gamma_faceDD00:4,
        gamma_faceDD01:5, gamma_faceDD02:6, gamma_faceDD11:7, gamma_faceDD12:8,
        gamma_faceDD22:9, gamma_faceUU00:4, gamma_faceUU01:5, gamma_faceUU02:6,
        gamma_faceUU11:7, gamma_faceUU12:8, gamma_faceUU22:9, phi:0,
        phi_face:0, psi6_temp:0, psi6center:0 )
    Wrote to file "GiRaFFE_staggered_standalone_Ccodes/boundary_conditions/EigenCoord_Cart_to_xx.h"

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

```python
# Part P0: Define REAL, set the number of ghost cells NGHOSTS (from NRPy+'s FD_CENTDERIVS_ORDER),
#          and set the CFL_FACTOR (which can be overwritten at the command line)

with open(os.path.join(Ccodesdir,"GiRaFFE_NRPy_REAL__NGHOSTS__CFL_FACTOR.h"), "w") as file:
    file.write("""
// Part P0.a: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER
#define NGHOSTS """+str(3)+"""
#define NGHOSTS_A2B """+str(2)+"""
// Part P0.b: Set the numerical precision (REAL) to double, ensuring all floating point
//            numbers are stored to at least ~16 significant digits
#define REAL """+REAL+"""
// Part P0.c: Set the CFL Factor. Can be overwritten at command line.
REAL CFL_FACTOR = """+str(default_CFL_FACTOR)+";")
```

```python
%%writefile $Ccodesdir/GiRaFFE_NRPy_standalone.c
// Step P0: Define REAL and NGHOSTS; and declare CFL_FACTOR. This header is generated in NRPy+.
#include "GiRaFFE_NRPy_REAL__NGHOSTS__CFL_FACTOR.h"

#include "declare_Cparameters_struct.h"

// Output parameters: output_every, min and max radii for L2 norm
const int NSKIP_OUTPUT = 1;
const REAL outer_integration_radius = 1.5; const REAL inner_integration_radius = 0.6;

// Step P1: Import needed header files
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#include "stdint.h" // Needed for Windows GCC 6.x compatibility
#ifndef M_PI
#define M_PI 3.141592653589793238462643383279502884L
#endif /*M_PI*/
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524400844362104849039L
#endif /*M_SQRT1_2*/

// Step P2: Declare the IDX4S(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_2NGHOSTS0 elements in memory. Similarly, consecutive values of
//           "k" are separated by Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1 in memory, etc.
#define IDX4S(g,i,j,k) \
( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) + Nxx_plus_2NGHOSTS2 * (g) ) ) )
#define IDX4ptS(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2) * (g) )
#define IDX3S(i,j,k) ( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) ) ) )
#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++)
#define LOOP_ALL_GFS_GPS(ii) _Pragma("omp parallel for") \
  for(int (ii)=0;(ii)<Nxx_plus_2NGHOSTS_tot*NUM_EVOL_GFS;(ii)++)

// Step P3: Set gridfunction macros
#include "boundary_conditions/gridfunction_defines.h"

// Step P4: Include the RHS, BC, and primitive recovery functions
#include "GiRaFFE_NRPy_Main_Driver.h"

// Step P5: Include the initial data functions
#include "set_initial_spacetime_metric_data.h"
#include "initial_data.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[]) {
    paramstruct params;
#include "set_Cparameters_default.h"
    // Step 0a: Read command-line input, error out if nonconformant
    if(argc != 4 || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < NGHOSTS) {
      printf("Error: Expected three command-line arguments: ./GiRaFFE_NRPy_standalone [Nx] [Ny] [Nz],\n");
      printf("where Nx is the number of grid points in the x direction, and so forth.\n");
      printf("Nx,Ny,Nz MUST BE larger than NGHOSTS (= %d)\n",NGHOSTS);
      exit(1);
    }
    // Step 0c: Set free parameters, overwriting Cparameters defaults
    //          by hand or with command-line input, as desired.
#include "free_parameters.h"
#include "set_Cparameters-nopointer.h"

    //          ... and then set up the numerical grid structure in time:
    const REAL t_final = 2.0;
    const REAL CFL_FACTOR = 0.5; // Set the CFL Factor

    // Step 0c: Allocate memory for gridfunctions
    const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;
    // Step 0k: Allocate memory for gridfunctions
#include "MoLtimestepping/RK_Allocate_Memory.h"
    REAL *restrict auxevol_gfs = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot);
    REAL *evol_gfs_exact = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot);
    REAL *auxevol_gfs_exact  = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot);

    // For debugging, it can be useful to set everything to NaN initially.
    if(poison_grids) {
        for(int ii=0;ii<NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
            y_n_gfs[ii] = 1.0/0.0;
            y_nplus1_running_total_gfs[ii] = 1.0/0.0;
            //k_odd_gfs[ii] = 1.0/0.0;
            //k_even_gfs[ii] = 1.0/0.0;
            diagnostic_output_gfs[ii] = 1.0/0.0;
            evol_gfs_exact[ii] = 1.0/0.0;
        }
        for(int ii=0;ii<NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
            auxevol_gfs[ii] = 1.0/0.0;
            auxevol_gfs_exact[ii] = 1.0/0.0;
        }
    }

    // Step 0d: Set up coordinates: Set dx, and then dt based on dx_min and CFL condition
    // This is probably already defined above, but just in case...
    #ifndef MIN
    #define MIN(A, B) ( ((A) < (B)) ? (A) : (B) )
    #endif
    REAL dt = CFL_FACTOR * MIN(dxx0,MIN(dxx1,dxx2)); // CFL condition
    int Nt = (int)(t_final / dt + 0.5); // The number of points in time.
                                        //Add 0.5 to account for C rounding down integers.

    // Step 0e: Set up cell-centered Cartesian coordinate grids
    REAL *xx[3];
    xx[0] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS0);
    xx[1] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS1);
    xx[2] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS2);
    for(int j=0;j<Nxx_plus_2NGHOSTS0;j++) xx[0][j] = xxmin[0] + (j-NGHOSTS+1)*dxx0;
    for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) xx[1][j] = xxmin[1] + (j-NGHOSTS+1)*dxx1;
    for(int j=0;j<Nxx_plus_2NGHOSTS2;j++) xx[2][j] = xxmin[2] + (j-NGHOSTS+1)*dxx2;

    // Step 1: Set up initial data to be exact solution at time=0:
    REAL time = 0.0;
    set_initial_spacetime_metric_data(&params,xx,auxevol_gfs);
    initial_data(&params,xx,auxevol_gfs,y_n_gfs);
    printf("vx = %.15e, %.15e, %.15e, %.15e\n",auxevol_gfs[IDX4S(VALENCIAVU0GF,0,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,1,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,2,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,3,35,35)]);

    // Code to perturb the initial data:
    /*
    for(int ii=0;ii<NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
        y_n_gfs[ii] += 1.0e-15;
        y_nplus1_running_total_gfs[ii] += 1.0e-15;
        //k_odd_gfs[ii] = 1.0/0.0;
        //k_even_gfs[ii] = 1.0/0.0;
        diagnostic_output_gfs[ii] += 1.0e-15;
        evol_gfs_exact[ii] += 1.0e-15;
    }
    for(int ii=0;ii<NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
        auxevol_gfs[ii] += 1.0e-15;
        auxevol_gfs_exact[ii] += 1.0e-15;
    }
    */

    // Fill in  the remaining quantities
    GiRaFFE_compute_B_and_Bstagger_from_A(&params,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD00GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD01GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD02GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD11GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD12GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*GAMMADD22GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*PSI6_TEMPGF, /* Temporary storage,overwritten */
                                          y_n_gfs+Nxx_plus_2NGHOSTS_tot*AD0GF,
                                          y_n_gfs+Nxx_plus_2NGHOSTS_tot*AD1GF,
                                          y_n_gfs+Nxx_plus_2NGHOSTS_tot*AD2GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BU0GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BU1GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BU2GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BSTAGGERU0GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BSTAGGERU1GF,
                                          auxevol_gfs+Nxx_plus_2NGHOSTS_tot*BSTAGGERU2GF);
    //override_BU_with_old_GiRaFFE(&params,auxevol_gfs,0);
    GiRaFFE_NRPy_prims_to_cons(&params,auxevol_gfs,y_n_gfs);
    // Extra stack, useful for debugging:
    printf("vx = %.15e, %.15e, %.15e, %.15e\n",auxevol_gfs[IDX4S(VALENCIAVU0GF,0,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,1,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,2,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,3,35,35)]);
    GiRaFFE_NRPy_cons_to_prims(&params,xx,auxevol_gfs,y_n_gfs);
    GiRaFFE_NRPy_prims_to_cons(&params,auxevol_gfs,y_n_gfs);
    GiRaFFE_NRPy_cons_to_prims(&params,xx,auxevol_gfs,y_n_gfs);
    GiRaFFE_NRPy_prims_to_cons(&params,auxevol_gfs,y_n_gfs);
    GiRaFFE_NRPy_cons_to_prims(&params,xx,auxevol_gfs,y_n_gfs);
    GiRaFFE_NRPy_prims_to_cons(&params,auxevol_gfs,y_n_gfs);
    //GiRaFFE_NRPy_cons_to_prims(&params,xx,auxevol_gfs,y_n_gfs);
    printf("vx = %.15e, %.15e, %.15e, %.15e\n",auxevol_gfs[IDX4S(VALENCIAVU0GF,0,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,1,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,2,35,35)],auxevol_gfs[IDX4S(VALENCIAVU0GF,3,35,35)]);

    // Fill in the exact gridfunctions the same way as above: In Exact Wald, initial data IS exact solution!
    for(int ii=0;ii<NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
        evol_gfs_exact[ii] = y_n_gfs[ii];
    }
    for(int ii=0;ii<NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot;ii++) {
        auxevol_gfs_exact[ii] = auxevol_gfs[ii];
    }

    for(int n=0;n<=Nt;n++) { // Main loop to progress forward in time.
    //for(int n=0;n<=1;n++) { // Main loop to progress forward in time.
        // Step 1a: Set current time to correct value & compute exact solution
        time = ((REAL)n)*dt;

        /* Step 2: Validation: Output relative error between numerical and exact solution, */
        if((n)%NSKIP_OUTPUT ==0) {
            // Step 2c: Output quantities along x axis.
            const int i0mid=Nxx_plus_2NGHOSTS0/2;
            const int i1mid=Nxx_plus_2NGHOSTS1/2;
            const int i2mid=Nxx_plus_2NGHOSTS2/2;
            char filename[100];
            sprintf(filename,"out%d-%08d.txt",Nxx0,n);
            FILE *out2D = fopen(filename, "w");
            for(int i0=0;i0<Nxx_plus_2NGHOSTS0;i0++) {
                const int idx = IDX3S(i0,i1mid,i2mid);
                fprintf(out2D,"%.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e\n",
                        xx[0][i0],
                        auxevol_gfs[IDX4ptS(BU0GF,idx)],auxevol_gfs[IDX4ptS(BU1GF,idx)],auxevol_gfs[IDX4ptS(BU2GF,idx)],
                        y_n_gfs[IDX4ptS(AD0GF,idx)],y_n_gfs[IDX4ptS(AD1GF,idx)],y_n_gfs[IDX4ptS(AD2GF,idx)],
                        y_n_gfs[IDX4ptS(STILDED0GF,idx)],y_n_gfs[IDX4ptS(STILDED1GF,idx)],y_n_gfs[IDX4ptS(STILDED2GF,idx)],
                        auxevol_gfs[IDX4ptS(ALPHAGF,idx)]*auxevol_gfs[IDX4ptS(VALENCIAVU0GF,idx)]-auxevol_gfs[IDX4ptS(BETAU0GF,idx)],
                        auxevol_gfs[IDX4ptS(ALPHAGF,idx)]*auxevol_gfs[IDX4ptS(VALENCIAVU1GF,idx)]-auxevol_gfs[IDX4ptS(BETAU1GF,idx)],
                        auxevol_gfs[IDX4ptS(ALPHAGF,idx)]*auxevol_gfs[IDX4ptS(VALENCIAVU2GF,idx)]-auxevol_gfs[IDX4ptS(BETAU2GF,idx)],
                        y_n_gfs[IDX4ptS(PSI6PHIGF,idx)]);
            }
            fclose(out2D);

            // Compute & output L2 norm. For each quantity Q we care about:
            // \sqrt{\frac{1}{N} \sum_{ijk} \left( Q_{\rm approx} - Q_{\rm exact} \right)^2}
            int num_quantities_integrated = 13;
            REAL integrated_quantities[num_quantities_integrated];
            for(int ii=0; ii<num_quantities_integrated; ii++) {
                integrated_quantities[ii] = 0.0;
            }
            sprintf(filename,"out%d-L2.txt",Nxx0);
            FILE *outL2 = fopen(filename, "a");
            int num_points = 0;
#ifndef SQR
#define SQR(x) ((x) * (x))
#endif /*SQR*/
            for(int i2=0;i2<Nxx_plus_2NGHOSTS2;i2++) {
                REAL xx2 = xx[2][i2];
                for(int i1=0;i1<Nxx_plus_2NGHOSTS1;i1++) {
                    REAL xx1 = xx[1][i1];
                    for(int i0=0;i0<Nxx_plus_2NGHOSTS0;i0++) {
                        REAL xx0 = xx[0][i0];
                        // Make sure we're inside a thick spherical shell:
                        if(SQR(xx0)+SQR(xx1)+SQR(xx2) < SQR(outer_integration_radius) &&
                           SQR(xx0)+SQR(xx1)+SQR(xx2) > SQR(inner_integration_radius)) {
                            const int idx = IDX3S(i0,i1,i2);
                            // Compute the sum of the squares of the difference between numerical and exact:
                            integrated_quantities[0]  += SQR(auxevol_gfs[IDX4ptS(BU0GF,idx)]         - auxevol_gfs_exact[IDX4ptS(BU0GF,idx)]);
                            integrated_quantities[1]  += SQR(auxevol_gfs[IDX4ptS(BU1GF,idx)]         - auxevol_gfs_exact[IDX4ptS(BU1GF,idx)]);
                            integrated_quantities[2]  += SQR(auxevol_gfs[IDX4ptS(BU2GF,idx)]         - auxevol_gfs_exact[IDX4ptS(BU2GF,idx)]);
                            integrated_quantities[3]  += SQR(y_n_gfs[IDX4ptS(AD0GF,idx)]             - evol_gfs_exact[IDX4ptS(AD0GF,idx)]);
                            integrated_quantities[4]  += SQR(y_n_gfs[IDX4ptS(AD1GF,idx)]             - evol_gfs_exact[IDX4ptS(AD1GF,idx)]);
                            integrated_quantities[5]  += SQR(y_n_gfs[IDX4ptS(AD2GF,idx)]             - evol_gfs_exact[IDX4ptS(AD2GF,idx)]);
                            integrated_quantities[6]  += SQR(y_n_gfs[IDX4ptS(STILDED0GF,idx)]        - evol_gfs_exact[IDX4ptS(STILDED0GF,idx)]);
                            integrated_quantities[7]  += SQR(y_n_gfs[IDX4ptS(STILDED1GF,idx)]        - evol_gfs_exact[IDX4ptS(STILDED1GF,idx)]);
                            integrated_quantities[8]  += SQR(y_n_gfs[IDX4ptS(STILDED2GF,idx)]        - evol_gfs_exact[IDX4ptS(STILDED2GF,idx)]);
                            integrated_quantities[9]  += SQR(auxevol_gfs[IDX4ptS(VALENCIAVU0GF,idx)] - auxevol_gfs_exact[IDX4ptS(VALENCIAVU0GF,idx)]);
                            integrated_quantities[10] += SQR(auxevol_gfs[IDX4ptS(VALENCIAVU1GF,idx)] - auxevol_gfs_exact[IDX4ptS(VALENCIAVU1GF,idx)]);
                            integrated_quantities[11] += SQR(auxevol_gfs[IDX4ptS(VALENCIAVU2GF,idx)] - auxevol_gfs_exact[IDX4ptS(VALENCIAVU2GF,idx)]);
                            integrated_quantities[12] += SQR(y_n_gfs[IDX4ptS(PSI6PHIGF,idx)]         - evol_gfs_exact[IDX4ptS(PSI6PHIGF,idx)]);
                            num_points++;
                        }
                    }
                }
            }
            // Divide by N, take the square root:
            for(int ii=0; ii<num_quantities_integrated; ii++) {
                integrated_quantities[ii] = sqrt(integrated_quantities[ii]/num_points);
            }
            fprintf(outL2,"%d %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e %.16e\n",
                    n,
                    integrated_quantities[0],integrated_quantities[1],integrated_quantities[2],
                    integrated_quantities[3],integrated_quantities[4],integrated_quantities[5],
                    integrated_quantities[6],integrated_quantities[7],integrated_quantities[8],
                    integrated_quantities[9],integrated_quantities[10],integrated_quantities[11],
                    integrated_quantities[12]);
            fclose(outL2);
        }

        // Step 3: Evolve scalar wave initial data forward in time using Method of Lines with RK4 algorithm,
        //         applying quadratic extrapolation outer boundary conditions.
        // Step 3.b: Step forward one timestep (t -> t+dt) in time using
        //           chosen RK-like MoL timestepping algorithm
#include "MoLtimestepping/RK_MoL.h"
    } // End main loop to progress forward in time.

    // Step 4: Free all allocated memory
#include "MoLtimestepping/RK_Free_Memory.h"
    free(auxevol_gfs);
    free(auxevol_gfs_exact);
    free(evol_gfs_exact);
    for(int i=0;i<3;i++) free(xx[i]);
    return 0;
}
```

    Writing GiRaFFE_staggered_standalone_Ccodes//GiRaFFE_NRPy_standalone.c

```python
cmd.C_compile(os.path.join(Ccodesdir,"GiRaFFE_NRPy_standalone.c"),
              os.path.join(Ccodesdir,"output","GiRaFFE_NRPy_standalone"),compile_mode="safe")
# !gcc -g -O2 -fopenmp GiRaFFE_standalone_Ccodes/GiRaFFE_NRPy_standalone.c -o GiRaFFE_NRPy_standalone -lm

# Change to output directory
os.chdir(outdir)
# Clean up existing output files
cmd.delete_existing_files("out*.txt")
cmd.delete_existing_files("out*.png")
# cmd.Execute(os.path.join(Ccodesdir,"output","GiRaFFE_NRPy_standalone"), "640 16 16", os.path.join(outdir,"out640.txt"))
cmd.Execute("GiRaFFE_NRPy_standalone", "64 64 64","out64.txt")
# cmd.Execute("GiRaFFE_NRPy_standalone", "128 128 128","out128.txt")
# !OMP_NUM_THREADS=1 valgrind --track-origins=yes -v ./GiRaFFE_NRPy_standalone 1280 32 32
# Return to root directory
os.chdir(os.path.join("../../"))
```

    Compiling executable...
    (EXEC): Executing `gcc -O2 -g -fopenmp GiRaFFE_staggered_standalone_Ccodes/GiRaFFE_NRPy_standalone.c -o GiRaFFE_staggered_standalone_Ccodes/output/GiRaFFE_NRPy_standalone -lm`...
    (BENCH): Finished executing in 2.614776611328125 seconds.
    Finished compilation.
    (EXEC): Executing `taskset -c 0,1,2,3 ./GiRaFFE_NRPy_standalone 64 64 64`...
    (BENCH): Finished executing in 117.32049703598022 seconds.

Now, we will load the data generated by the simulation and plot it in order to test for convergence.

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

#          0   1     2     3     4     5     6     7          8          9          10            11            12             13
labels = ["x","BU0","BU1","BU2","AD0","AD1","AD2","StildeD0","StildeD1","StildeD2","ValenciavU0","ValenciavU1","ValenciavU2", "psi6Phi"]
# Data_numer = np.loadtxt(os.path.join(Ccodesdir,"output","out64-00000010.txt"))
# # Data_num_2 = np.loadtxt(os.path.join(Ccodesdir,"output","out239-00000080.txt"))
# # Data_old = np.loadtxt("/home/penelson/OldCactus/Cactus/exe/ABE-GiRaFFEfood_1D_AlfvenWave/giraffe-grmhd_primitives_bi.x.asc")
# # Data_o_2 = np.loadtxt("/home/penelson/OldCactus/Cactus/exe/ABE-GiRaFFEfood_1D_AlfvenWave_2/giraffe-grmhd_primitives_bi.x.asc")
# # Data_numer = Data_old[5000:5125,11:15] # The column range is chosen for compatibility with the plotting script.
# # Data_num_2 = Data_o_2[19600:19845,11:15] # The column range is chosen for compatibility with the plotting script.
# Data_exact = np.loadtxt(os.path.join(Ccodesdir,"output","out64-00000000.txt"))
# # Data_exa_2 = np.loadtxt(os.path.join(Ccodesdir,"output","out239-00000080_exact.txt"))

# predicted_order = 2.0
# column = 8
# plt.figure()
# # # plt.plot(Data_exact[2:-2,0],np.log2(np.absolute((Data_numer[2:-2,column]-Data_exact[2:-2,column])/\
# # #                                     (Data_num_2[2:-2:2,column]-Data_exa_2[2:-2:2,column]))),'.')
# plt.plot(Data_exact[0:22,0],Data_exact[0:22,column],'b')
# plt.plot(Data_exact[49:-1,0],Data_exact[49:-1,column],'b')
# plt.plot(Data_exact[0:22,0],Data_numer[0:22,column],'r.')
# plt.plot(Data_exact[49:-1,0],Data_numer[49:-1,column],'r.')
# # plt.xlim(-0.0,1.0)
# # # plt.ylim(-1.0,5.0)
# # # plt.ylim(-0.0005,0.0005)
# plt.xlabel(labels[0])
# plt.ylabel(labels[column])
# plt.show()

# Plotting scripts for comparison with original GiRaFFE:
old_files = ["",
             "giraffe-grmhd_primitives_bi.x.asc","giraffe-grmhd_primitives_bi.x.asc","giraffe-grmhd_primitives_bi.x.asc",
             "giraffe-em_ax.x.asc","giraffe-em_ay.x.asc","giraffe-em_az.x.asc",
             "giraffe-grmhd_conservatives.x.asc","giraffe-grmhd_conservatives.x.asc","giraffe-grmhd_conservatives.x.asc",
             "giraffe-grmhd_primitives_allbutbi.x.asc","giraffe-grmhd_primitives_allbutbi.x.asc","giraffe-grmhd_primitives_allbutbi.x.asc",
             "giraffe-em_psi6phi.x.asc"]
column = 6
column_old = [0,12,13,14,12,12,12,12,13,14,12,13,14,12]
old_path = "/home/penelson/Cactus/exe/ABE-GiRaFFEfood_ExactWald_newcode_agreement"
perturb_path = "/home/penelson/Cactus/exe/ABE-GiRaFFEfood_ExactWald_newcode_agreement_pert"
new_path = os.path.join(Ccodesdir,"output")
data_old = np.loadtxt(os.path.join(old_path,old_files[column]))
data_per = np.loadtxt(os.path.join(perturb_path,old_files[column]))
n=80
data_old = data_old[n*70:n*70+70,:]# Select only the nth timestep
data_per = data_per[n*70:n*70+70,:]# Select only the nth timestep
data_new = np.loadtxt(os.path.join(new_path,"out64-00000080.txt"))

# deltaA_old = data_old[125:250,:] - data_old[0:125,:]
# data_new_t0 = np.loadtxt(os.path.join(new_path,"out119-00000000.txt"))
# deltaA_new = data_new[:,:] - data_new_t0[:,:]

plt.figure()
plt.semilogy(data_new[:,0],(data_new[:,column]-data_old[:,column_old[column]])/(data_new[:,column]+data_old[:,column_old[column]]),label="Old vs. New")

# plt.plot(data_new[4:22,0],data_new[4:22,column]-data_old[4:22,column_old[column]])
# plt.plot(data_new[49:-4,0],data_new[49:-4,column]-data_old[49:-4,column_old[column]])

# plt.plot(data_new[:,0],data_old[:,column_old[column]])
# plt.plot(data_new[:,0],data_new[:,column],'.')

# plt.plot(data_new[:22,0],data_old[:22,column_old[column]])
# plt.plot(data_new[49:,0],data_old[49:,column_old[column]])
# plt.plot(data_new[:22,0],data_new[:22,column],'.')
# plt.plot(data_new[49:,0],data_new[49:,column],'.')

# plt.plot(data_new[3:-3,0],deltaA_new[3:-3,column],'.')
# plt.plot(data_new[3:-3,0],deltaA_old[3:-3,column_old[column]]-deltaA_new[3:-3,column])
# plt.xlabel(labels[0])
# plt.ylabel(labels[column])
# plt.show()

# plt.figure()
plt.semilogy(data_new[:,0],(data_old[:,column_old[column]]-data_per[:,column_old[column]])/(data_old[:,column_old[column]]+data_per[:,column_old[column]]),label="Old vs. Perturbed")
plt.xlabel(labels[0])
plt.ylabel("E_rel in "+labels[column])
plt.legend()
plt.show()
```

This code will create an animation of the wave over time.

```python
# import matplotlib.pyplot as plt
from matplotlib.pyplot import savefig
from IPython.display import HTML
import matplotlib.image as mgimg

import glob
import sys
from matplotlib import animation

cmd.delete_existing_files("out64-00*.png")
globby = glob.glob(os.path.join(Ccodesdir,'output','out64-00*.txt'))
file_list = []
for x in sorted(globby):
    file_list.append(x)

number_of_files = int(len(file_list))

for timestep in range(number_of_files):
    fig = plt.figure()
    numer_filename = file_list[timestep]
    exact_filename = os.path.join(Ccodesdir,'output','out64-00000000.txt')
    Numer = np.loadtxt(numer_filename)
    Exact = np.loadtxt(exact_filename)

    plt.title("Exact Wald")
    plt.xlabel("x")
    plt.ylabel("BU2")
#     plt.xlim(-0.5,0.5)
#     plt.ylim(1.0,1.7)

    plt.plot(Numer[3:22,0],Numer[3:22,3],'.',label="Numerical")
    plt.plot(Numer[49:-3,0],Numer[49:-3,3],'.',label="Numerical")
    plt.plot(Exact[3:22,0],Exact[3:22,3],label="Exact")
    plt.plot(Exact[49:-3,0],Exact[49:-3,3],label="Exact")
    plt.legend()
    savefig(numer_filename+".png",dpi=150)
    plt.close(fig)
    sys.stdout.write("%c[2K" % 27)
    sys.stdout.write("Processing file "+numer_filename+"\r")
    sys.stdout.flush()
```

    Processing file GiRaFFE_staggered_standalone_Ccodes/output/out64-00000087.txt

```python
## VISUALIZATION ANIMATION, PART 2: Combine PNGs to generate movie ##
# https://stackoverflow.com/questions/14908576/how-to-remove-frame-from-matplotlib-pyplot-figure-vs-matplotlib-figure-frame
# https://stackoverflow.com/questions/23176161/animating-pngs-in-matplotlib-using-artistanimation
# !rm -f GiRaFFE_NRPy-1D_tests.mp4
cmd.delete_existing_files("GiRaFFE_NRPy-3D_tests.mp4")

fig = plt.figure(frameon=False)
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

myimages = []

for i in range(number_of_files):
    img = mgimg.imread(file_list[i]+".png")
    imgplot = plt.imshow(img)
    myimages.append([imgplot])

ani = animation.ArtistAnimation(fig, myimages, interval=100,  repeat_delay=1000)
plt.close()
ani.save('GiRaFFE_NRPy-1D_tests.mp4', fps=5,dpi=150)
```

```python
%%HTML

```

```python
import cmdline_helper as cmd    # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-GiRaFFE_NRPy_Main_Driver",location_of_template_file=os.path.join(".."))
```

    Created Tutorial-GiRaFFE_NRPy_Main_Driver.tex, and compiled LaTeX file to
        PDF file Tutorial-GiRaFFE_NRPy_Main_Driver.pdf


```python
pip install tensorflow==2.4.1 tensorflow-quantum
```

```python
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
```

    <module 'pkg_resources' from '/usr/local/lib/python3.7/dist-packages/pkg_resources/__init__.py'>

```python

```

```python

```

```python

```

```python

from google.colab import drive
drive.mount('/content/drive')
```

    Mounted at /content/drive

```python
import sys
sys.path.append('/content/drive/My Drive')
```

```python
import eecs598
import torch
import torchvision
import matplotlib.pyplot as plt
import statistics
import numpy as np
```

```python
import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np
import seaborn as sns
import collections

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
```

```python

# Control qrid size for visualization
plt.rcParams['figure.figsize'] = (10.0, 8.0)
plt.rcParams['font.size'] = 16
```

```python
x_train, y_train, x_test, y_test = eecs598.data.cifar10()

print('Training set:', )
print('  data shape:', x_train.shape)
print('  labels shape: ', y_train.shape)
print('Test set:')
print('  data shape: ', x_test.shape)
print('  labels shape', y_test.shape)
```

    Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./cifar-10-python.tar.gz

      0%|          | 0/170498071 [00:00<?, ?it/s]

    Extracting ./cifar-10-python.tar.gz to .
    Training set:
      data shape: torch.Size([50000, 3, 32, 32])
      labels shape:  torch.Size([50000])
    Test set:
      data shape:  torch.Size([10000, 3, 32, 32])
      labels shape torch.Size([10000])

```python
import random
from torchvision.utils import make_grid

classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
samples_per_class = 12
samples = []
for y, cls in enumerate(classes):
    plt.text(-4, 34 * y + 18, cls, ha='right')
    idxs, = (y_train == y).nonzero(as_tuple=True)
    for i in range(samples_per_class):
        idx = idxs[random.randrange(idxs.shape[0])].item()
        samples.append(x_train[idx])
img = torchvision.utils.make_grid(samples, nrow=samples_per_class)
plt.imshow(eecs598.tensor_to_image(img))
plt.axis('off')
plt.show()
```

```python

```

```python
from tensorflow.keras.datasets import cifar10
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
```

    Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
    170500096/170498071 [==============================] - 6s 0us/step

```python
y_train=train_labels.flatten()
y_test=test_labels.flatten()
# We have reduced the dimension
```

```python
x_train=tf.image.rgb_to_grayscale(train_images)
x_test=tf.image.rgb_to_grayscale(test_images)
#to convert grayscale
```

```python
def filter_36(x, y):
    keep = (y == 3) | (y == 6)
    x, y = x[keep], y[keep]
    y = y == 3
    return x,y
```

```python
x_train, y_train = filter_36(x_train, y_train)
x_test, y_test = filter_36(x_test, y_test)

print("Number of filtered training examples:", len(x_train))
print("Number of filtered test examples:", len(x_test))
```

    Number of filtered training examples: 10000
    Number of filtered test examples: 2000

```python
plt.imshow(x_train[0, :, :, 0])
plt.colorbar()
```

```python
x_train_s = tf.image.resize(x_train, (4,4)).numpy()
x_test_s = tf.image.resize(x_test, (4,4)).numpy()
```

```python

THRESHOLD = 0.5

x_train_bin = np.array(x_train_s > THRESHOLD, dtype=np.float32)
x_test_bin = np.array(x_test_s > THRESHOLD, dtype=np.float32)
```

```python
qubits = cirq.GridQubit.rect(4, 4)

```

```python
xx=cirq.Y(qubits[1])

```

```python
cirq.X(qubits[1])
cirq.Y(qubits[1])
cirq.Z
```

    Y((0, 1))

```python
np.random.uniform(size=(4, 3))
```

    array([[0.7967969 , 0.30777665, 0.44788952],
           [0.18320573, 0.26701107, 0.49590493],
           [0.05564715, 0.00566145, 0.81495824],
           [0.77629675, 0.28633108, 0.49930407]])

```python
cirq.Circuit(cirq.X(qubits[1]), cirq.Y(qubits[1]))
```

(0, 1): ───X───Y───

```python

```

```python
def convert_to_circuit(image):
    """Encode truncated classical image into quantum datapoint."""
    values = np.ndarray.flatten(image)
    qubits = cirq.GridQubit.rect(6, 6)
    circuit = cirq.Circuit()
    for i, value in enumerate(values):
        if value:
            cirq.X(qubits[i])
            circuit.append((cirq.X(qubits[i]), cirq.Y(qubits[i]), cirq.Z(qubits[i])   ))

    return circuit

x_train_circ = [convert_to_circuit(x) for x in x_train_bin]
x_test_circ = [convert_to_circuit(x) for x in x_test_bin]
```

```python
SVGCircuit(x_train_circ[0])

```

    findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

```python
x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)
```

```python
class CircuitLayerBuilder():
    def __init__(self, data_qubits, readout):
        self.data_qubits = data_qubits
        self.readout = readout

    def add_layer(self, circuit, gate, prefix):
        for i, qubit in enumerate(self.data_qubits):
            symbol = sympy.Symbol(prefix + '-' + str(i))
            circuit.append(gate(qubit, self.readout)**symbol)
```

```python
demo_builder = CircuitLayerBuilder(data_qubits = cirq.GridQubit.rect(4,1),
                                   readout=cirq.GridQubit(-1,-1))

circuit = cirq.Circuit()
demo_builder.add_layer(circuit, gate = cirq.XX, prefix='xx')
SVGCircuit(circuit)
```

```python
def create_quantum_model():
    """Create a QNN model circuit and readout operation to go along with it."""
    data_qubits = cirq.GridQubit.rect(4, 4)  # a 4x4 grid.
    readout = cirq.GridQubit(-1, -1)         # a single qubit at [-1,-1]
    circuit = cirq.Circuit()

    # Prepare the readout qubit.
    circuit.append(cirq.X(readout))
    circuit.append(cirq.H(readout))

    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout=readout)

    # Then add layers (experiment by adding more).
    builder.add_layer(circuit, cirq.XX, "xx1")
    builder.add_layer(circuit, cirq.ZZ, "zz1")

    # Finally, prepare the readout qubit.
    circuit.append(cirq.H(readout))

    return circuit, cirq.Z(readout)
```

```python
model_circuit, model_readout = create_quantum_model()
```

```python
model = tf.keras.Sequential([
    # The input is the data-circuit, encoded as a tf.string
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    # The PQC layer returns the expected value of the readout gate, range [-1,1].
    tfq.layers.PQC(model_circuit, model_readout),
])
```

```python
y_train_hinge = 2.0*y_train-1.0
y_test_hinge = 2.0*y_test-1.0

```

```python
def hinge_accuracy(y_true, y_pred):
    y_true = tf.squeeze(y_true) > 0.0
    y_pred = tf.squeeze(y_pred) > 0.0
    result = tf.cast(y_true == y_pred, tf.float32)

    return tf.reduce_mean(result)
```

```python
model.compile(
    loss=tf.keras.losses.Hinge(),
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[hinge_accuracy])

```

```python
print(model.summary())
```

    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    pqc (PQC)                    (None, 1)                 32
    =================================================================
    Total params: 32
    Trainable params: 32
    Non-trainable params: 0
    _________________________________________________________________
    None

```python
EPOCHS = 3
BATCH_SIZE = 128

NUM_EXAMPLES = len(x_train_tfcirc)

```

```python
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES]
y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]

```

```python
import time
start_time = time.time()
```

```python
qnn_history = model.fit(
      x_train_tfcirc_sub, y_train_hinge_sub,
      batch_size=32,
      epochs=EPOCHS,
      verbose=1,
      validation_data=(x_test_tfcirc, y_test_hinge))

qnn_results = model.evaluate(x_test_tfcirc, y_test)

```

    Epoch 1/3
     12/313 [>.............................] - ETA: 3:09:25 - loss: 1.0009 - hinge_accuracy: 0.5213


# KVLCC2 Ikeda method

# Purpose
How good is original Ikeda method for this ship?

# Methodology
Run PyScoresII and calculate Ikeda

# WIP - improvements
(WORK IN PROGRESS)
Use this section only if the notebook is not final.

Notable TODOs:
* todo 1
* todo 2
* todo 3

## Results
Describe and comment the most important results.

# Suggested next steps
State suggested next steps, based on results obtained in this notebook.

# Setup

```python
# %load imports.py
"""
These is the standard setup for the notebooks.
"""

%matplotlib inline
%load_ext autoreload
%autoreload 2

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

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 collections import OrderedDict
#plt.style.use('paper')

#import data
import copy
from mdldb.run import Run

from sklearn.pipeline import Pipeline
from rolldecayestimators.transformers import CutTransformer, LowpassFilterDerivatorTransformer, ScaleFactorTransformer, OffsetTransformer
from rolldecayestimators.direct_estimator_cubic import EstimatorQuadraticB, EstimatorCubic
from rolldecayestimators.ikeda_estimator import IkedaQuadraticEstimator
import rolldecayestimators.equations as equations
import rolldecayestimators.lambdas as lambdas
from rolldecayestimators.substitute_dynamic_symbols import lambdify
import rolldecayestimators.symbols as symbols
import sympy as sp

from sympy.physics.vector.printing import vpprint, vlatex
from IPython.display import display, Math, Latex

from sklearn.metrics import r2_score
from src.data import database
from mdldb import tables

```

```python
import pyscores2
import pyscores2.runScores2
import pyscores2.xml_hydrostatics
from pyscores2.output import OutputFile
from rolldecayestimators.ikeda import Ikeda, IkedaR

from rolldecayestimators.simplified_ikeda_class import SimplifiedIkeda, SimplifiedIkedaABS
from rolldecayestimators.simplified_ikeda import limits_kawahara
from pyscores2.runScores2 import Calculation
import shutil
```

```python
db = database.get_db()
```

```python
sql = """
SELECT * from run
WHERE model_number='M5057-01-A' and test_type='roll decay' and project_number=40178362;
"""
df_rolldecays = pd.read_sql(sql=sql, con=db.engine, index_col='id')

df_rolldecays['ship_speed'].fillna(0, inplace=True)

```

```python
df_rolldecays=df_rolldecays.loc[[21337,21338]].copy()
```

```python
row = df_rolldecays.iloc[0]
run = db.session.query(Run).get(int(row.name))
run = database.load_run(run, save_as_example=False, prefer_hdf5=True)

```

```python
print(run.project.project_path)
```

## Run ScoresII

```python
xml_parser = pyscores2.xml_hydrostatics.Parser(fileName='../data/external/KVLCC2m_kbk_final_ScoresData.xml')
indata = xml_parser.convertToScores2Indata(conditionName='Design')
indata.runOptions["IJ"].set_value(1)
indata.runOptions["IK"].set_value(2)
```

```python
indata.kxx = run.loading_condition.KXX*0.78  # To get correct natural frequency
indata.kyy = run.loading_condition.KZZ
indata.speedMax=15.5
indata.speedIncrement=15
indata.waveFrequenciesMax = 1.0
indata.waveFrequenciesMin = 0.3
indata.waveFrequenciesIncrement = 0.015
#indata.zcg = run.loading_condition.kg
```

```python
indata.save('../models/KVLCC2_0speed.IN')
```

```python
save_dir_name = 'scores'
if not os.path.exists(save_dir_name):
    os.mkdir(save_dir_name)

calculation = Calculation(outDataDirectory='scores')
```

```python
calculation.run(indata=indata)
```

## Load ScoresII results

```python
output_file = OutputFile(filePath=calculation.outDataPath)
```

```python
shutil.copyfile(calculation.outDataPath,'../data/interim/KVLCC2_0speed.out')
```

```python
output_file.results
```

```python
df = output_file.get_result()
```

```python
df.describe()
```

```python
df.head()
```

```python
df[r'lambda/lpp'] = df['waveLengths']/run.ship.lpp

fig,ax=plt.subplots()
for index, group in df.groupby(by=['speed','wave direction']):
    group.plot(x=r'lambda/lpp', y='heaveAmplitude', style='o-', label=index, ax=ax)

ax.grid(True)
ax.legend();
ax.set_ylabel('Heave');
```

```python
RAO_15_0 = df.groupby(by=['speed','wave direction']).get_group((15,180))
```

```python
fig,ax=plt.subplots()
RAO_15_0.plot(x=r'lambda/lpp', y='heaveAmplitude', style='o-', ax=ax)
ax.set_xlim((0,1.8))

fig,ax=plt.subplots()
RAO_15_0.plot(x=r'lambda/lpp', y='pitchAmplitude', style='o-', ax=ax)
#ax.set_xlim((0,1.8))
```

```python
df_roll_damping = output_file.get_roll_damping()
df_roll_damping
```

## Run Ikeda

```python
w = 2.462149630662348

scale_factor=run.model.scale_factor
V = row.ship_speed*1.852/3.6/np.sqrt(scale_factor)

if not run.ship.BKL:
    BKL=0
else:
    BKL=run.ship.BKL/scale_factor

if not run.ship.BKB:
    BKB = 0
else:
    BKB=run.ship.BKB/scale_factor

#fi_as = np.deg2rad([1,10])
fi_as = np.deg2rad(10)

ikeda = Ikeda.load_scoresII(V=V, w=w, fi_a=fi_as, indata=indata, output_file=output_file,
                            scale_factor=scale_factor, BKL=BKL, BKB=BKB)

R = 0.05*run.ship.beam/scale_factor  # Just guessing...
ikeda.R = R
```

```python
def calculate_ikeda(ikeda):

    output = pd.DataFrame()
    output['B_44_hat']   = ikeda.calculate_B44()
    output['B_W0_hat']   = float(ikeda.calculate_B_W0())
    output['B_W_hat']    = float(ikeda.calculate_B_W())
    output['B_F_hat']    = ikeda.calculate_B_F()
    output['B_E_hat']    = ikeda.calculate_B_E()
    output['B_BK_hat']   = ikeda.calculate_B_BK()
    output['B_L_hat']    = float(ikeda.calculate_B_L())
    output['Bw_div_Bw0'] = float(ikeda.calculate_Bw_div_Bw0())
    return output
```

```python
result_datas = calculate_ikeda(ikeda)  # DataFrame with two roll amplitudes
```

```python
result_datas
```

## Simplified Ikeda also...

```python
lpp = run.ship.lpp/scale_factor
beam = run.ship.beam/scale_factor
kg = run.loading_condition.kg/scale_factor
volume = run.loading_condition.Volume/(scale_factor**3)
draught = (run.loading_condition.TA + run.loading_condition.TF)/2/scale_factor
A0 = run.loading_condition.A0

if not run.ship.BKL:
    BKL=0
else:
    BKL = run.ship.BKL

if not run.ship.BKB:
    BKB = 0
else:
    BKB = run.ship.BKB

si = SimplifiedIkeda(V=V, w=w, fi_a=fi_as, beam=beam, lpp=lpp, kg = kg, volume=volume, draught=draught, A0=A0, BKL=BKL, BKB=BKB)
```

```python
def calculate_SI(si):

    output = pd.DataFrame()
    output['B_44_hat'] = si.calculate_B44()
    output['B_W0_hat'] =si.calculate_B_W0()
    output['B_W_hat'] =si.calculate_B_W()
    output['B_F_hat'] =si.calculate_B_F()
    output['B_E_hat'] =si.calculate_B_E()
    output['B_BK_hat'] =si.calculate_B_BK()
    output['B_L_hat'] =si.calculate_B_L()
    output['Bw_div_Bw0'] =si.calculate_Bw_div_Bw0()

    return output
```

```python
result_datas_SI = calculate_SI(si=si)
```

```python
result_datas_SI
```

```python
df_results = pd.DataFrame(columns=result_datas.columns)
df_results.loc['ikeda']=result_datas.iloc[0]
df_results.loc['SI']=result_datas_SI.iloc[0]

```

```python
interesting = ['B_W_hat','B_F_hat','B_E_hat']
df_results[interesting].plot(kind='bar',stacked=True)
```

```python
limits_kawahara
```

```python
df_limits = pd.DataFrame(data = limits_kawahara, index = ['min','max']).transpose()
df_limits
```

```python
g=9.81
omega_hat = lambdas.omega_hat(beam=beam, g=g, omega0=w)
```

```python
Cb = volume/(lpp*beam*draught)
OG = draught-kg
ship_limits = {
 'CB': Cb,
 'B/d': beam/draught,
 'OG/d': OG/draught,
 'CMID': A0,
 'bBk/B': BKB/beam,
 'lBk/LPP': BKL/beam,
 'OMEGA_hat': omega_hat}
ship_limits = pd.Series(ship_limits,name='ship')
```

```python
df_limits['ship'] = ship_limits
```

```python
df_limits_clean = df_limits.copy()
if df_limits.loc['bBk/B','ship']==0:
    df_limits_clean.drop('bBk/B', inplace=True)

if df_limits.loc['lBk/LPP','ship']==0:
    df_limits_clean.drop('lBk/LPP', inplace=True)
```

```python
fig,ax=plt.subplots()
ax.errorbar(df_limits_clean.index,df_limits_clean['ship'],yerr=[df_limits_clean['ship']-df_limits_clean['min'],df_limits_clean['max']-df_limits_clean['ship']],
            fmt='ok', lw=1, ecolor='gray', capsize=20)
ax.set_title('Ship vs. SI limits')
```

```python
df_ = df_limits_clean.sub(df_limits['min'],axis=0)
df_limits_normalized = df_.div(df_['max'], axis=0)
```

```python
fig,ax=plt.subplots()
ax.errorbar(df_limits_normalized.index,df_limits_normalized['ship'],yerr=[df_limits_normalized['ship']-df_limits_normalized['min'],
                                                                         df_limits_normalized['max']-df_limits_normalized['ship']], fmt='ok', lw=1, ecolor='gray', capsize=20)
ax.set_title('Ship vs. SI limits')
ax.set_ylabel('Norlimized limit')
```

```python

```