GiRaFFE_NRPy
C code library: Boundary conditions¶GiRaFFE_NRPy
uses to apply boundary conditions to the GRFFE quantities.¶Notebook Status: Validated
Validation Notes: This code has been validated by confirming that it produces exactly the expected output to round-off precision for arbitrary linear inputs.
The functions and macros defined here fall into one of two categories. The first we will work with is the FACE_UPDATE
family, which will act on a single face, looping over each point as defined by the parameters i0min
, i0max
, i1min
, i1max
, i2min
, and i2max
. The parameters FACEX0
, FACEX1
, and FACEX2
define which face on which we will act; that is, two of the FACEX
parameters must be set to NUL
(defined as 0) while the third is set to either MAXFACE
(defined as -1) or MINFACE
(defined as +1). For instance, if we want to fill in a ghostzone on the +x face of our grid, we must call FACE_UPDATE
with FACEX0 = MAXFACE
, FACEX1 = NUL
, and FACEX2 = NUL
.
Care must be taken to set i0min
, i0max
, i1min
, i1max
, i2min
, and i2max
in such a way as to be consistent with FACEX0
, FACEX1
, and FACEX2
; failure to do so can result in bad data and out-of-bounds errors. This is handled by the function apply_bcs
, which is a part of the second family of functions. Functions of this type are responsible for doing so on each face in each ghostzone.
This notebook is organized as follows
# Step 0: Add NRPy's directory to the path
# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
import os, sys # Standard Python modules for multiplatform OS-level functions
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
sys.path.append(nrpy_dir_path)
import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
outdir = os.path.join("GiRaFFE_NRPy","GiRaFFE_Ccode_validation","boundary_conditions")
cmd.mkdir(outdir)
The first FACE_UPDATE
macro will be basic linear extrapolation conditions. It will apply boundary conditions in the specified ghostzone for the gridfunction specified by which_gf
in the array gfs
. That array will be passed under that name into the functions that call FACE_UPDATE
; by convention, gfs
is the array of evolved gridfunctions.
%%writefile $outdir/GiRaFFE_boundary_conditions.h
// Currently, we're using basic Cartesian boundary conditions, pending fixes by Zach.
// Part P8a: Declare boundary condition FACE_UPDATE macro,
// which updates a single face of the 3D grid cube
// using quadratic polynomial extrapolation.
// Basic extrapolation boundary conditions
#define FACE_UPDATE(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \
gfs[IDX4S(which_gf,i0,i1,i2)] = \
+2.0*gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)] \
-1.0*gfs[IDX4S(which_gf,i0+2*FACEX0,i1+2*FACEX1,i2+2*FACEX2)]; \
}
// +1.0*gfs[IDX4S(which_gf,i0+3*FACEX0,i1+3*FACEX1,i2+3*FACEX2)]; \
Writing GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
This macro, FACE_UPDATE_COPY
, applies copy boundary conditions. Instead of a linear extrapolation of the data in the nearest two points in the direction of the grid interior, it simply copies the data from the nearest point in the direction of the grid interior.
We also define MAXFACE
, NUL
, and MINFACE
as constants. These should be unchanging and accessible to any function anywhere in the program.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
// Basic Copy boundary conditions
#define FACE_UPDATE_COPY(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \
gfs[IDX4S(which_gf,i0,i1,i2)] = gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)]; \
}
// Part P8b: Boundary condition driver routine: Apply BCs to all six
// boundary faces of the cube, filling in the innermost
// ghost zone first, and moving outward.
const int MAXFACE = -1;
const int NUL = +0;
const int MINFACE = +1;
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
This macro, FACE_UPDATE_OUTFLOW
, is poorly named at the moment; currently, it is a clone of the macro FACE_UPDATE
that acts on the array aux_gfs
instead of gfs
. However, take note of the commented code below the macro these lines can be used to implement outflow boundary conditions with linear extrapolation. For that algorithm, the macro will accept a which_gf_0
parameter instead of which_gf
, and operate on the gridfunctions which_gf_0+0
, which_gf_0+1
, and which_gf_0+2
(that is, the macro will act on an entire 3-vector). This must be done since the different faces and components must be handled in slightly different ways.
In (actual) outflow boundary conditions, if a quantity is directed inwards (e.g. if vx<0 in the +x ghostzone), it is set to zero. Otherwise, we apply the standard linear extrapolation boundary condition.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
// This macro acts differently in that it acts on an entire 3-vector of gfs, instead of 1.
// which_gf_0 corresponds to the zeroth component of that vector. The if statements only
// evaluate true if the velocity is directed inwards on the face in consideration.
#define FACE_UPDATE_OUTFLOW(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \
aux_gfs[IDX4S(which_gf_0,i0,i1,i2)] = \
aux_gfs[IDX4S(which_gf_0,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \
aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] = \
aux_gfs[IDX4S(which_gf_0+1,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \
aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] = \
aux_gfs[IDX4S(which_gf_0+2,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \
}
/* if(FACEX0*aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] > 0.0) { \
aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] = 0.0; \
} \
if(FACEX1*aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] > 0.0) { \
aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] = 0.0; \
} \
if(FACEX2*aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] > 0.0) { \
aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] = 0.0; \
} \
*/
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
The second category of functions we use here is responsible for applying BCs in all the ghostzones by applying the FACE_UPDATE
macro in the correct manner for each ghostzone on each face. So, we loop over each evolved gridfunction (that is not a component of StildeD
); for each gridfunction, we first define the parameters imin
and imax
to specify the area just outside the interior of the grid. We then call FACE_UPDATE
on each face of the innermost ghostzone. As we go, we'll decrement each component of imin
and increment each component of imax
; thus, after we have done all six faces, imin
and imax
specify the next-innermost ghostzone. We proceed in this manner until we have covered each ghostzone.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
void apply_bcs_potential(const paramstruct *restrict params,REAL *gfs) {
#include "../set_Cparameters.h"
// First, we apply extrapolation boundary conditions to AD
#pragma omp parallel for
for(int which_gf=0;which_gf<NUM_EVOL_GFS;which_gf++) {
if(which_gf < STILDED0GF || which_gf > STILDED2GF) {
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
FACE_UPDATE(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
FACE_UPDATE(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;
FACE_UPDATE(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
FACE_UPDATE(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;
FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
imin[2]--;
FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
imax[2]++;
}
}
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
This code works almost identically to the above. It applies copy boundary conditions, but is currently not in use.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
// Then, we apply copy boundary conditions to StildeD and psi6Phi
/*#pragma omp parallel for
for(int which_gf=3;which_gf<NUM_EVOL_GFS;which_gf++) {
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
FACE_UPDATE_COPY(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
FACE_UPDATE_COPY(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;
FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;
FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--;
FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++;
}
}*/
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
Once again, this next set of loops operates almost identically as above, but it applies BCs to the velocities instead.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
void apply_bcs_velocity(const paramstruct *restrict params,REAL *aux_gfs) {
#include "../set_Cparameters.h"
// Apply outflow/copy boundary conditions to ValenciavU by passing VALENCIAVU0 as which_gf_0
// for(int which_gf=VALENCIAVU0GF;which_gf<=VALENCIAVU2GF;which_gf++) {
const int which_gf_0 = VALENCIAVU0GF;
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
FACE_UPDATE_OUTFLOW(which_gf_0, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
FACE_UPDATE_OUTFLOW(which_gf_0, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;
FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;
FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
imin[2]--;
FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
imax[2]++;
}
// }
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
The next algorithms we will cover are exact boundary conditions. These are a testing tool that we can use to determine if our boundary conditions are causing a problem - since we know the exact solution to the Alfvén wave at any future time, we can simply set the boundary conditions to this value.
IMPORTANT: Since we have gauge freedom in specifying the vector potential Ai, this vector can drift in a way that has no physical effect on the system, but will cause massive inconsistencies between the ghostzones and grid interior that will propagate when taking derivatives of Ai.
Note that FACE_UPDATE_EXACT
is a function, not a macro; this is necessary because macros will not let us include header files containing the equations that we wish to use here. This forces us to pass more parameters than we did before. This function works similarly to the initial data function we use, but instead loops over a more limited portion of the grid, as determined by parameters passed from within apply_bcs_EXACT
. Note also that when we define the x coordinate xx0
, we shift it by the expected distance the wave should have travelled. TODO: also multiply by the wavespeed
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
/*// A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions
void FACE_UPDATE_EXACT(const paramstruct *restrict params,REAL *restrict xx[3],
const int n, const REAL dt,REAL *out_gfs,REAL *aux_gfs,
const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max,
const int FACEX0,const int FACEX1,const int FACEX2) {
#include "../set_Cparameters.h"
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) {
REAL xx0 = xx[0][i0]-n*dt;
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];
if(xx0<=lbound) {
#include "../GiRaFFEfood_A_v_1D_tests_left.h"
}
else if (xx0<rbound) {
#include "../GiRaFFEfood_A_v_1D_tests_center.h"
}
else {
#include "../GiRaFFEfood_A_v_1D_tests_right.h"
}
out_gfs[IDX4S(PSI6PHIGF, i0,i1,i2)] = 0.0;
}
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
This function is, once again, almost identical to the first portion of apply_bcs
. The primary difference is the version of FACE_UPDATE
it calls; furthermore, since FACE_UPDATE_EXACT
operates on several gridfunctions simultaneously, it is not necessary to loop over gridfunctions.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
void apply_bcs_EXACT(const paramstruct *restrict params,REAL *restrict xx[3],
const int n, const REAL dt,
REAL *out_gfs,REAL *aux_gfs) {
#include "../set_Cparameters.h"
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
// Right now, we only want to update the xmin and xmax faces with the exact data.
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL);
imin[0]--;
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL);
imax[0]++;
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL);
imin[1]--;
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL);
imax[1]++;
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
imin[2]--;
FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
imax[2]++;
}
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
[Back to top]
This function covers the gap in the above algorithm by applying exact boundary conditions to StildeD
. There are two different options given here: one includes the header file that is used in the initial data setup to calculate StildeD
from the 3-velocity and magnetic field the other assumes that this step was already done when out_gfs_exact
was filled at the current timestep (the *_exact
arrays are filled at each timestep with the exact solution to allow for convergence testing) and copies the data from there.
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
// A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions
void FACE_UPDATE_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3],
REAL *out_gfs,REAL *out_gfs_exact,
const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max,
const int FACEX0,const int FACEX1,const int FACEX2) {
#include "../set_Cparameters.h"
// This is currently modified to calculate more exact boundary conditions for StildeD. Rename if it works.
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) {
#include "../GiRaFFEfood_NRPy_Stilde.h"
}
idx = IDX3(i0,i1,i2);
out_gfs[IDX4ptS(STILDED0GF,idx)] = out_gfs_exact[IDX4ptS(STILDED0GF,idx)];
out_gfs[IDX4ptS(STILDED1GF,idx)] = out_gfs_exact[IDX4ptS(STILDED1GF,idx)];
out_gfs[IDX4ptS(STILDED2GF,idx)] = out_gfs_exact[IDX4ptS(STILDED2GF,idx)];
}
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
%%writefile -a $outdir/GiRaFFE_boundary_conditions.h
void apply_bcs_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3],
REAL *out_gfs,REAL *out_gfs_exact) {
#include "../set_Cparameters.h"
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
// Right now, we only want to update the xmin and xmax faces with the exact data.
FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL);
imin[0]--;
FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL);
imax[0]++;
//FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL);
imin[1]--;
//FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL);
imax[1]++;
//FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
imin[2]--;
//FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
imax[2]++;
}
}*/
Appending to GiRaFFE_NRPy/GiRaFFE_Ccode_validation/boundary_conditions/GiRaFFE_boundary_conditions.h
import difflib
import sys
# Define the directory that we wish to validate against:
valdir = os.path.join("GiRaFFE_NRPy","GiRaFFE_Ccode_library","boundary_conditions")
import GiRaFFE_NRPy.GiRaFFE_NRPy_BCs as BC
BC.GiRaFFE_NRPy_BCs(valdir)
print("Printing difference between original C code and this code...")
# Open the files to compare
files_to_check = ["GiRaFFE_boundary_conditions.h"]
for file in files_to_check:
print("Checking file " + file)
with open(os.path.join(valdir,file)) as file1, open(os.path.join(outdir,file)) as file2:
# Read the lines of each file
file1_lines = file1.readlines()
file2_lines = file2.readlines()
num_diffs = 0
for line in difflib.unified_diff(file1_lines, file2_lines, fromfile=os.path.join(valdir+file), tofile=os.path.join(outdir+file)):
sys.stdout.writelines(line)
num_diffs = num_diffs + 1
if num_diffs == 0:
print("No difference. TEST PASSED!")
else:
print("ERROR: Disagreement found with .py file. See differences above.")
Printing difference between original C code and this code... Checking file GiRaFFE_boundary_conditions.h No difference. TEST PASSED!
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-GiRaFFE_NRPy-BCs.pdf (Note that clicking on this link may not work; you may need to open the PDF file through another means.)
import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-GiRaFFE_NRPy-BCs")
Created Tutorial-GiRaFFE_NRPy-BCs.tex, and compiled LaTeX file to PDF file Tutorial-GiRaFFE_NRPy-BCs.pdf