<jupyter_start><jupyter_text>Unit Testing `GiRaFFE_NRPy`: Fluxes of $\tilde{S}_i$ Author: <NAME>This notebook validates our new, NRPyfied HLLE solver against the function from the original `GiRaFFE` that calculates the flux for $\tilde{S}_i$ according to the the method of Harten, Lax, von Leer, and Einfeldt (HLLE), assuming that we have calculated the values of the flux on the cell faces according to the piecewise-parabolic method (PPM) of [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf), modified for the case of GRFFE. **Module Status:**  Validated:  This code has passed unit tests against the original `GiRaFFE` version.**Validation Notes:** Once this is completed, it will show the validation of [Tutorial-GiRaFFE_NRPy_Ccode_library-Stilde-flux](../Tutorial-GiRaFFE_NRPy_Ccode_library-Stilde-flux.ipynb).It is, in general, good coding practice to unit test functions individually to verify that they produce the expected and intended output. Here, we expect our functions `GRFFE__S_*__flux.C` to produce identical output to the function `GRFFE__S_i__flux.C` in the original `GiRaFFE`. It should be noted that the two codes handle the parameter `flux_dirn` (the direction in which the code is presently calculating the flux through the cell) differently; in the original `GiRaFFE`, the function `GRFFE__S_i__flux()` expects a parameter `flux_dirn` with value 1, 2, or 3, corresponding to the functions `GRFFE__S_0__flux()`, `GRFFE__S_1__flux()`, and `GRFFE__S_2__flux()`, respectively, in `GiRaFFE_NRPy`. We'll write this in C because the codes we want to test are already written that way, and we would like to avoid modifying the files as much as possible. To do so, we will write the C code as a string literal, and then print it to a file. We will begin by including core functionality. We will also define standard parameters needed for GRFFE and NRPy+.<jupyter_code>out_string = """
// These are common packages that we are likely to need.
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "string.h" // Needed for strncmp, etc.
#include "stdint.h" // Needed for Windows GCC 6.x compatibility
#include <time.h>   // Needed to set a random seed.

// Standard GRFFE parameters:
const double GAMMA_SPEED_LIMIT = 10.0;
const int Nxx_plus_2NGHOSTS[3] = {1,1,1};

// Standard NRPy+ memory access:
#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) )

"""<jupyter_output><empty_output><jupyter_text>Now, we'll manually write the gridfunction `define`s that NRPy+ needs.<jupyter_code>out_string += """
// Let's also #define the NRPy+ gridfunctions
#define ALPHA_FACEGF 0
#define GAMMADET_FACEGF 1
#define GAMMA_FACEDD00GF 2
#define GAMMA_FACEDD01GF 3
#define GAMMA_FACEDD02GF 4
#define GAMMA_FACEDD11GF 5
#define GAMMA_FACEDD12GF 6
#define GAMMA_FACEDD22GF 7
#define GAMMA_FACEUU00GF 8
#define GAMMA_FACEUU11GF 9
#define GAMMA_FACEUU22GF 10
#define BETA_FACEU0GF 11
#define BETA_FACEU1GF 12
#define BETA_FACEU2GF 13
#define VALENCIAV_RU0GF 14
#define VALENCIAV_RU1GF 15
#define VALENCIAV_RU2GF 16
#define B_RU0GF 17
#define B_RU1GF 18
#define B_RU2GF 19
#define VALENCIAV_LU0GF 20
#define VALENCIAV_LU1GF 21
#define VALENCIAV_LU2GF 22
#define B_LU0GF 23
#define B_LU1GF 24
#define B_LU2GF 25
#define U4UPPERZERO_LGF 26
#define U4UPPERZERO_RGF 27
#define STILDE_FLUXD0GF 28
#define STILDE_FLUXD1GF 29
#define STILDE_FLUXD2GF 30
#define NUM_AUXEVOL_GFS 31

"""<jupyter_output><empty_output><jupyter_text>The last things NRPy+ will require are the definition of type `REAL` and, of course, the functions we are testing. These files should be generated on the fly, but we'll not do that until there are far fewer changes to be made in the notebook.<jupyter_code>out_string += """
// The NRPy+ versions of the function. These should require relatively little modification.
// We will need this define, though:
#define REAL double
#include "../PPM/GRFFE__S_0__flux.C"
#include "../PPM/GRFFE__S_1__flux.C"
#include "../PPM/GRFFE__S_2__flux.C"

"""<jupyter_output><empty_output><jupyter_text>Next, we'll include the files from the old `GiRaFFE`. But before we can do so, we should define modified versions of the CCTK macros.<jupyter_code>out_string += """
#define CCTK_REAL double
#define DECLARE_CCTK_PARAMETERS //
struct cGH{};
const cGH cctkGH;

"""<jupyter_output><empty_output><jupyter_text>We'll also need to download the files in question from the `GiRaFFE` bitbucket repository. This code was originally written by <NAME> in the IllinoisGRMHD documentation; we have modified it to download the files we want. Of note is the addition of the `for` loop since we need three files (The function `GRFFE__S_i__flux()` depends on two other files for headers and functions).<jupyter_code># First download the original IllinoisGRMHD source code
import os,sys
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

import urllib
import os
import cmdline_helper as cmd

out_dir  = "Validation"
cmd.mkdir(out_dir)

original_file_url  = ["https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/GiRaFFE/src/GiRaFFE_headers.h",\
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/GiRaFFE/src/inlined_functions.C",\
                      "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/GiRaFFE/src/GRFFE__S_i__flux.C"\
                     ]
original_file_name = ["GiRaFFE_headers-original.h",\
                      "inlined_functions-original.C",\
                      "GRFFE__S_i__flux-original.C"\
                     ]

for i in range(len(original_file_url)):
    original_file_path = os.path.join(out_dir,original_file_name[i])

    # Then download the original IllinoisGRMHD source code
    # We try it here in a couple of ways in an attempt to keep
    # the code more portable
    try:
        original_file_code = urllib.request.urlopen(original_file_url[i]).read()
    except:
        original_file_code = urllib.urlopen(original_file_url[i]).read()

    # Write down the file the original IllinoisGRMHD source code
    with open(original_file_path,"w") as file:
        file.write(original_file_code)
    
    # We add the following lines to append includes to the code we're writing
    out_string += """#include \""""
    out_string += original_file_name[i]
    out_string +=""""
"""<jupyter_output><empty_output><jupyter_text>Now we can write a main function. In this function, we will fill all relevant arrays with (appropriate) random values. That is, if a certain gridfunction should never be negative, we will make sure to only generate positive numbers for it. We must also contend with the fact that in NRPy+, we chose to use the Valencia 3-velocity $v^i_{(n)}$, while in ETK, we used  the drift velocity $v^i$; the two are related by $$v^i = \alpha v^i_{(n)} - \beta^i.$$<jupyter_code>out_string +="""
int main() {
    // We'll define all indices to be 0. No need to complicate memory access
    const int i0 = 0;
    const int i1 = 0;
    const int i2 = 0;
    
    // This is the array to which we'll write the NRPy+ variables.
    REAL *auxevol_gfs  = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS);
    
    // These are the arrays to which we will write the ETK variables.
    CCTK_REAL METRIC_LAP_PSI4[NUMVARS_METRIC_AUX];
    CCTK_REAL Ur[MAXNUMVARS];
    CCTK_REAL Ul[MAXNUMVARS];
    CCTK_REAL FACEVAL[NUMVARS_FOR_METRIC_FACEVALS];
    CCTK_REAL cmax; CCTK_REAL cmin;
    CCTK_REAL st_x_flux; CCTK_REAL st_y_flux; CCTK_REAL st_z_flux;
    
    // Now, it's time to make the random numbers.
    const long int seed = 1570632212;//time(NULL);
    //srand(seed); // Set the seed
    printf("seed = %ld\\n",seed);
    // We take care to make sure the corresponding quantities have the SAME value.
    auxevol_gfs[IDX4(ALPHA_FACEGF, i0,i1,i2)] = (double)rand()/RAND_MAX;
    const double alpha = auxevol_gfs[IDX4(ALPHA_FACEGF, i0,i1,i2)];
    METRIC_LAP_PSI4[LAPSE] = alpha;
    //METRIC_LAP_PSI4[LAPM1] = METRIC_LAP_PSI4[LAPSE]-1;
        
    auxevol_gfs[IDX4(GAMMA_FACEDD00GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4(GAMMA_FACEDD01GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4(GAMMA_FACEDD02GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4(GAMMA_FACEDD11GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4(GAMMA_FACEDD12GF, i0,i1,i2)] = (double)rand()/RAND_MAX*0.2-0.1;
    auxevol_gfs[IDX4(GAMMA_FACEDD22GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*0.2-0.1;
    
    // Generated by NRPy+:
       const double gammaDD00 = auxevol_gfs[IDX4(GAMMA_FACEDD00GF, i0,i1,i2)];
       const double gammaDD01 = auxevol_gfs[IDX4(GAMMA_FACEDD01GF, i0,i1,i2)];
       const double gammaDD02 = auxevol_gfs[IDX4(GAMMA_FACEDD02GF, i0,i1,i2)];
       const double gammaDD11 = auxevol_gfs[IDX4(GAMMA_FACEDD11GF, i0,i1,i2)];
       const double gammaDD12 = auxevol_gfs[IDX4(GAMMA_FACEDD12GF, i0,i1,i2)];
       const double gammaDD22 = auxevol_gfs[IDX4(GAMMA_FACEDD22GF, i0,i1,i2)];
       /* 
        * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory:
        */
       const double tmp0 = gammaDD11*gammaDD22;
       const double tmp1 = pow(gammaDD12, 2);
       const double tmp2 = gammaDD02*gammaDD12;
       const double tmp3 = pow(gammaDD01, 2);
       const double tmp4 = pow(gammaDD02, 2);
       const double tmp5 = gammaDD00*tmp0 - gammaDD00*tmp1 + 2*gammaDD01*tmp2 - gammaDD11*tmp4 - gammaDD22*tmp3;
       const double tmp6 = 1.0/tmp5;
       auxevol_gfs[IDX4(GAMMA_FACEUU00GF, i0, i1, i2)] = tmp6*(tmp0 - tmp1);
       auxevol_gfs[IDX4(GAMMA_FACEUU11GF, i0, i1, i2)] = tmp6*(gammaDD00*gammaDD22 - tmp4);
       auxevol_gfs[IDX4(GAMMA_FACEUU22GF, i0, i1, i2)] = tmp6*(gammaDD00*gammaDD11 - tmp3);
       auxevol_gfs[IDX4(GAMMADET_FACEGF, i0, i1, i2)] = tmp5;

    auxevol_gfs[IDX4(GAMMADET_FACEGF, i0,i1,i2)] = tmp5;
    METRIC_LAP_PSI4[PSI6] = sqrt(auxevol_gfs[IDX4(GAMMADET_FACEGF, i0,i1,i2)]);
    METRIC_LAP_PSI4[PSI2] = pow(METRIC_LAP_PSI4[PSI6],1.0/3.0);
    METRIC_LAP_PSI4[PSI4] = METRIC_LAP_PSI4[PSI2]*METRIC_LAP_PSI4[PSI2];
    const double Psim4 = 1.0/METRIC_LAP_PSI4[PSI4];
    METRIC_LAP_PSI4[PSIM4] = Psim4;

    // Copied from the ETK implementation 
        CCTK_REAL gtxxL = gammaDD00*Psim4;
        CCTK_REAL gtxyL = gammaDD01*Psim4;
        CCTK_REAL gtxzL = gammaDD02*Psim4;
        CCTK_REAL gtyyL = gammaDD11*Psim4;
        CCTK_REAL gtyzL = gammaDD12*Psim4;
        CCTK_REAL gtzzL = gammaDD22*Psim4;
        
        /*********************************
         * Apply det gtij = 1 constraint *
         *********************************/
        const CCTK_REAL gtijdet = gtxxL * gtyyL * gtzzL + gtxyL * gtyzL * gtxzL + gtxzL * gtxyL * gtyzL - 
          gtxzL * gtyyL * gtxzL - gtxyL * gtxyL * gtzzL - gtxxL * gtyzL * gtyzL;

        /*const CCTK_REAL gtijdet_Fm1o3 = fabs(1.0/cbrt(gtijdet));

        gtxxL = gtxxL * gtijdet_Fm1o3;
        gtxyL = gtxyL * gtijdet_Fm1o3; 
        gtxzL = gtxzL * gtijdet_Fm1o3; 
        gtyyL = gtyyL * gtijdet_Fm1o3; 
        gtyzL = gtyzL * gtijdet_Fm1o3; 
        gtzzL = gtzzL * gtijdet_Fm1o3;*/
                
    FACEVAL[GXX] = gtxxL;
    FACEVAL[GXY] = gtxyL;
    FACEVAL[GXZ] = gtxzL;
    FACEVAL[GYY] = gtyyL;
    FACEVAL[GYZ] = gtyzL;
    FACEVAL[GZZ] = gtzzL;

    FACEVAL[GUPXX] = ( gtyyL * gtzzL - gtyzL * gtyzL )/gtijdet;
    FACEVAL[GUPYY] = ( gtxxL * gtzzL - gtxzL * gtxzL )/gtijdet;
    FACEVAL[GUPZZ] = ( gtxxL * gtyyL - gtxyL * gtxyL )/gtijdet;
    
    auxevol_gfs[IDX4(BETA_FACEU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    const double betax = auxevol_gfs[IDX4(BETA_FACEU0GF, i0,i1,i2)];
    FACEVAL[SHIFTX] = betax;
    auxevol_gfs[IDX4(BETA_FACEU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    const double betay = auxevol_gfs[IDX4(BETA_FACEU1GF, i0,i1,i2)];
    FACEVAL[SHIFTY] = betay;
    auxevol_gfs[IDX4(BETA_FACEU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    const double betaz = auxevol_gfs[IDX4(BETA_FACEU2GF, i0,i1,i2)];
    FACEVAL[SHIFTZ] = betaz;
    
    /*auxevol_gfs[IDX4(VALENCIAV_RU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[VX] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU0GF, i0,i1,i2)]-betax;
    auxevol_gfs[IDX4(VALENCIAV_RU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[VY] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU1GF, i0,i1,i2)]-betay;
    auxevol_gfs[IDX4(VALENCIAV_RU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[VZ] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU2GF, i0,i1,i2)]-betaz;*/
    /* Near-luminal */
    auxevol_gfs[IDX4(VALENCIAV_RU0GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ur[VX] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU0GF, i0,i1,i2)]-betax;
    auxevol_gfs[IDX4(VALENCIAV_RU1GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ur[VY] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU1GF, i0,i1,i2)]-betay;
    auxevol_gfs[IDX4(VALENCIAV_RU2GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ur[VZ] = alpha*auxevol_gfs[IDX4(VALENCIAV_RU2GF, i0,i1,i2)]-betaz;
    
    auxevol_gfs[IDX4(B_RU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[BX_CENTER] = auxevol_gfs[IDX4(B_RU0GF, i0,i1,i2)];
    auxevol_gfs[IDX4(B_RU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[BY_CENTER] = auxevol_gfs[IDX4(B_RU1GF, i0,i1,i2)];
    auxevol_gfs[IDX4(B_RU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ur[BZ_CENTER] = auxevol_gfs[IDX4(B_RU2GF, i0,i1,i2)];
    
    /*auxevol_gfs[IDX4(VALENCIAV_LU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[VX] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU0GF, i0,i1,i2)]-betax;
    auxevol_gfs[IDX4(VALENCIAV_LU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[VY] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU1GF, i0,i1,i2)]-betay;
    auxevol_gfs[IDX4(VALENCIAV_LU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[VZ] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU2GF, i0,i1,i2)]-betaz;*/
    /* Near-luminal */
    auxevol_gfs[IDX4(VALENCIAV_LU0GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ul[VX] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU0GF, i0,i1,i2)]-betax;
    auxevol_gfs[IDX4(VALENCIAV_LU1GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ul[VY] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU1GF, i0,i1,i2)]-betay;
    auxevol_gfs[IDX4(VALENCIAV_LU2GF, i0,i1,i2)] = 1.0+(double)rand()/RAND_MAX*9.0;
    Ul[VZ] = alpha*auxevol_gfs[IDX4(VALENCIAV_LU2GF, i0,i1,i2)]-betaz;
    
    auxevol_gfs[IDX4(B_LU0GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[BX_CENTER] = auxevol_gfs[IDX4(B_LU0GF, i0,i1,i2)];
    auxevol_gfs[IDX4(B_LU1GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[BY_CENTER] = auxevol_gfs[IDX4(B_LU1GF, i0,i1,i2)];
    auxevol_gfs[IDX4(B_LU2GF, i0,i1,i2)] = (double)rand()/RAND_MAX*2.0-1.0;
    Ul[BZ_CENTER] = auxevol_gfs[IDX4(B_LU2GF, i0,i1,i2)];
    
    
    printf("NRPy+ speeds: %.4e, %.4e, %.4e, %.4e, %.4e, %.4e\\n",auxevol_gfs[IDX4(VALENCIAV_RU0GF, i0,i1,i2)],auxevol_gfs[IDX4(VALENCIAV_RU1GF, i0,i1,i2)],auxevol_gfs[IDX4(VALENCIAV_RU2GF, i0,i1,i2)],auxevol_gfs[IDX4(VALENCIAV_LU0GF, i0,i1,i2)],auxevol_gfs[IDX4(VALENCIAV_LU1GF, i0,i1,i2)],auxevol_gfs[IDX4(VALENCIAV_LU2GF, i0,i1,i2)]);
    printf("ETK   speeds: %.4e, %.4e, %.4e, %.4e, %.4e, %.4e\\n\\n",Ur[VX],Ur[VY],Ur[VZ],Ul[VX],Ul[VY],Ul[VZ]);
    // Now, we'll run the NRPy+ and ETK functions, once in each flux_dirn.
    // We'll compare the output in-between each 
    GRFFE__S_0__flux(0,0,0, auxevol_gfs);
    GRFFE__S_i__flux(0,0,0,1,Ul,Ur,FACEVAL,METRIC_LAP_PSI4,cmax,cmin,st_x_flux,st_y_flux,st_z_flux); 
    printf("NRPy+ Results: %.16e, %.16e, %.16e\\n",auxevol_gfs[IDX4(STILDE_FLUXD0GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD1GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD2GF, i0,i1,i2)]);
    printf("ETK   Results: %.16e, %.16e, %.16e\\n\\n",st_x_flux,st_y_flux,st_z_flux);
    
    GRFFE__S_1__flux(0,0,0, auxevol_gfs);
    GRFFE__S_i__flux(0,0,0,2,Ul,Ur,FACEVAL,METRIC_LAP_PSI4,cmax,cmin,st_x_flux,st_y_flux,st_z_flux);    
    printf("NRPy+ Results: %.16e, %.16e, %.16e\\n",auxevol_gfs[IDX4(STILDE_FLUXD0GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD1GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD2GF, i0,i1,i2)]);
    printf("ETK   Results: %.16e, %.16e, %.16e\\n\\n",st_x_flux,st_y_flux,st_z_flux);

    
    GRFFE__S_2__flux(0,0,0, auxevol_gfs);
    GRFFE__S_i__flux(0,0,0,3,Ul,Ur,FACEVAL,METRIC_LAP_PSI4,cmax,cmin,st_x_flux,st_y_flux,st_z_flux);    
    printf("NRPy+ Results: %.16e, %.16e, %.16e\\n",auxevol_gfs[IDX4(STILDE_FLUXD0GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD1GF, i0,i1,i2)],auxevol_gfs[IDX4(STILDE_FLUXD2GF, i0,i1,i2)]);
    printf("ETK   Results: %.16e, %.16e, %.16e\\n\\n",st_x_flux,st_y_flux,st_z_flux);

}
"""<jupyter_output><empty_output><jupyter_text>Finally, we will write out the string that we have build as a C file.<jupyter_code>import cmdline_helper as cmd
import time

with open(os.path.join(out_dir,"Stilde_flux_unit_test.C"),"w") as file:
    file.write(out_string)<jupyter_output><empty_output><jupyter_text>And now, we will compile and run the C code. We also make python calls to time how long each of these steps takes.<jupyter_code>print("Now compiling, should take ~2 seconds...\n")
start = time.time()
cmd.C_compile(os.path.join(out_dir,"Stilde_flux_unit_test.C"), os.path.join(out_dir,"Stilde_flux_unit_test"))
end = time.time()
print("Finished in "+str(end-start)+" seconds.\n\n")

# os.chdir(out_dir)
print("Now running...\n")
start = time.time()
# cmd.Execute(os.path.join("Stilde_flux_unit_test"))
!./Validation/Stilde_flux_unit_test
end = time.time()
print("Finished in "+str(end-start)+" seconds.\n\n")
# os.chdir(os.path.join("../"))<jupyter_output>Now compiling, should take ~2 seconds...

Compiling executable...
Executing `gcc -Ofast -fopenmp -march=native -funroll-loops Validation/Stilde_flux_unit_test.C -o Validation/Stilde_flux_unit_test -lm`...
Finished executing in 0.415111064911 seconds.
Finished compilation.
Finished in 0.424943208694 seconds.


Now running...

seed = 1570632212
NRPy+ speeds: 5.2966e+00, 6.6598e+00, 4.2831e+00, 6.7214e+00, 7.4557e+00, 2.2744e+00
ETK   speeds: 3.9137e+00, 6.0400e+00, 3.4906e+00, 5.1108e+00, 6.7086e+00, 1.8030e+00

NRPy+ Results: 3.1564839773270723e-02, 1.6535228583654242e-03, 2.8679105226593151e-03
ETK   Results: 3.1564839773723596e-02, 1.6535228588904858e-03, 2.8679105227932059e-03

NRPy+ Results: 4.2071891842984481e-02, 2.1162206370742868e-03, -1.7540985696470991e-02
ETK   Results: 4.2071891844866184e-02, 2.1162206392760521e-03, -1.7540985695910203e-02

NRPy+ Results: -1.0092747989448396e-02, -2.6961744486436307e-02, 1.3459561640998659e-02
ETK   Results: -1.0092747988731540e-02, -2.69617[...]
{'fraction_non_alphanumeric': 0.13821606068532566, 'fraction_numerical': 0.07292702066439968, 'mean_word_length': 4.544083526682135, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 35, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '413626', 'license': 'BSD-2-Clause', 'n_tokens_mistral': 9379, 'n_tokens_neox': 8548, 'n_words': 1550, 'path': 'GiRaFFE_standalone_Ccodes/Tutorial-Unit_Test-GiRaFFE_NRPy_Ccode_library-Stilde-flux.ipynb', 'repo_name': 'dinatraykova/nrpytutorial', 'size': '19128'}