{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "# Interpolating Metric Quantities on Cell Faces\n", "\n", "## Author: Patrick Nelson\n", "\n", "**Notebook Status:** Validated\n", "\n", "**Validation Notes:** This module will be self-validated against [its module](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_FCVAL.py) and will also be validated against the corresponding algorithm in the old `GiRaFFE` code in [this tutorial](Tutorial-Start_to_Finish-GiRaFFE_NRPy-FCVAL.ipynb).\n", "\n", "### This module presents the functionality of [GiRaFFE_NRPy_Metric_Face_Values.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_Metric_Face_Values.py) .\n", "This notebook presents the macros from the original `GiRaFFE` that provide the values of the metric gridfunctions interpolated to the cell faces along with the code needed to implement this in NRPy. \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Table of Contents\n", "$$\\label{toc}$$\n", "\n", "This notebook is organized as follows\n", "\n", "0. [Step 0](#prelim): Preliminaries\n", "1. [Step 1](#interpolator): The Interpolator\n", " 1. [Step 1.a](#macros): Interpolator coefficients and definition\n", " 1. [Step 1.b](#gf_struct): Create an array to easily define the gridfunctions we want to interpolate.\n", " 1. [Step 1.c](#loops): Define the loop parameters and body\n", "1. [Step 2](#code_validation): Code Validation against original C code\n", "1. [Step 3](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 0: Preliminaries \\[Back to [top](#toc)\\]\n", "$$\\label{prelim}$$\n", "\n", "This first block of code just sets up a subdirectory within `GiRaFFE_standalone_Ccodes/` to which we will write the C code and adds core NRPy+ functionality to `sys.path`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Step 0: Add NRPy's directory to the path\n", "# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory\n", "import os,sys\n", "nrpy_dir_path = os.path.join(\"..\")\n", "if nrpy_dir_path not in sys.path:\n", " sys.path.append(nrpy_dir_path)\n", "\n", "from outputC import outCfunction # NRPy+: Core C code output module\n", "import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n", "Ccodesdir = \"GiRaFFE_standalone_Ccodes/FCVAL\"\n", "cmd.mkdir(os.path.join(Ccodesdir))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 1: The Interpolator \\[Back to [top](#toc)\\]\n", "$$\\label{interpolator}$$\n", "\n", "Here, we we will write the code necessary to interpolate the metric gridfunction $\\alpha, \\beta^i, \\gamma_{ij}$ onto the cell faces. These values will be necessary to compute fluxes of the Poynting vector and electric field through those faces.\n", "\n", "\n", "\n", "## Step 1.a: Interpolator coefficients and definition \\[Back to [top](#toc)\\]\n", "$$\\label{macros}$$\n", "\n", "First, we will define the functional form of our interpolator. At some point on our grid $i$, we will calculate the value of some gridfunction $Q$ at position $i-\\tfrac{1}{2}$ with\n", "$$\n", "Q_{i-1/2} = a_{i-2} Q_{i-2} +a_{i-1} Q_{i-1} +a_{i} Q_{i} +a_{i+1} Q_{i+1}\n", "$$\n", "and the coefficients we will use for it, \n", "\\begin{align}\n", "a_{i-2} &= -0.0625 \\\\\n", "a_{i-1} &= 0.5625 \\\\\n", "a_{i} &= 0.5625 \\\\\n", "a_{i+1} &= -0.0625 \\\\\n", "\\end{align}." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting GiRaFFE_standalone_Ccodes/FCVAL/interpolate_metric_gfs_to_cell_faces.h\n" ] } ], "source": [ "%%writefile $Ccodesdir/interpolate_metric_gfs_to_cell_faces.h\n", "// Side note: the following values could be used for cell averaged gfs:\n", "// am2=-1.0/12.0, am1=7.0/12.0, a0=7.0/12.0, a1=-1.0/12.0\n", "// However, since the metric gfs store the grid point values instead of the cell average,\n", "// the following coefficients should be used:\n", "// am2 = -1/16, am1 = 9/16, a0 = 9/16, a1 = -1/16\n", "// This will yield the third-order-accurate face values at m-1/2,\n", "// using values specified at {m-2,m-1,m,m+1}\n", "#define AM2 -0.0625\n", "#define AM1 0.5625\n", "#define A0 0.5625\n", "#define A1 -0.0625\n", "#define COMPUTE_FCVAL(METRICm2,METRICm1,METRIC,METRICp1) (AM2*(METRICm2) + AM1*(METRICm1) + A0*(METRIC) + A1*(METRICp1))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 1.b: Create an array to easily define the gridfunctions we want to interpolate. \\[Back to [top](#toc)\\]\n", "$$\\label{gf_struct}$$\n", "\n", "We will need to apply this interpolation to each gridpoint for several gridfunctions: the lapse $\\alpha$, the shift $\\beta^i$, and the three-metric $\\gamma_{ij}$. Consider that in NRPy+, each gridfunction is assigned an integer identifier with the C macro `#define`. So, the simplest (and shortest to write!) way to ensure we hit each of these is to create arrays that list each of these identifiers in order, so we can always hit the write gridfunction no matter where each gridfunction lies in the list. We use two arrays; the first identifies the usual gridfunctions from which we will read, and the second identifies the face-sampled gridfunctions to which we will write. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Appending to GiRaFFE_standalone_Ccodes/FCVAL/interpolate_metric_gfs_to_cell_faces.h\n" ] } ], "source": [ "%%writefile -a $Ccodesdir/interpolate_metric_gfs_to_cell_faces.h\n", "\n", "const int metric_gfs_list[10] = {GAMMADD00GF,\n", " GAMMADD01GF,\n", " GAMMADD02GF,\n", " GAMMADD11GF,\n", " GAMMADD12GF,\n", " GAMMADD22GF,\n", " BETAU0GF,\n", " BETAU1GF,\n", " BETAU2GF,\n", " ALPHAGF};\n", "\n", "const int metric_gfs_face_list[10] = {GAMMA_FACEDD00GF,\n", " GAMMA_FACEDD01GF,\n", " GAMMA_FACEDD02GF,\n", " GAMMA_FACEDD11GF,\n", " GAMMA_FACEDD12GF,\n", " GAMMA_FACEDD22GF,\n", " BETA_FACEU0GF,\n", " BETA_FACEU1GF,\n", " BETA_FACEU2GF,\n", " ALPHA_FACEGF};\n", "\n", "const int num_metric_gfs = 10;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 1.c: Define the loop parameters and body \\[Back to [top](#toc)\\]\n", "$$\\label{loops}$$\n", "\n", "Next, we will write the function that loops over the entire grid. One additional parameter to consider here is the direction in which we need to do the interpolation. This direction exactly corresponds to the parameter `flux_dirn` used in the calculation of the flux of the [Poynting vector](Tutorial-GiRaFFE_NRPy-Stilde-flux.ipynb) and [electric field](Tutorial-GiRaFFE_NRPy-Induction_Equation.ipynb).\n", "\n", "The outermost loop will iterate over the gridfunctions we listed above. Nested inside of that, there will be three loops that go through the grid in the usual way. However, the upper bound will be a little unusual. Instead of covering all points or all interior points, we will write these loops to cover all interior points *and one extra past that*. This is because we have defined our interpolation on the $i-\\tfrac{1}{2}$ face of a cell, but any given calculation will require both that and an interpolation on the $i+\\tfrac{1}{2}$ face as well." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# FIXME: The old oop bounds are NGHOSTS to Nxx+NGHOSTS+1, but converting reconstructed velocities to\n", "# and from Valencia requires extra points, so we crank it to maximum.\n", "desc = \"Interpolate metric gridfunctions to cell faces\"\n", "name = \"interpolate_metric_gfs_to_cell_faces\"\n", "interp_Cfunc = outCfunction(\n", " outfile = \"returnstring\", desc=desc, name=name,\n", " params =\"const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn\",\n", " preloop =\"\"\" int in_gf,out_gf;\n", " REAL Qm2,Qm1,Qp0,Qp1;\n", "\n", "\"\"\" ,\n", " body =\"\"\" for(int gf = 0;gf < num_metric_gfs;gf++) {\n", " in_gf = metric_gfs_list[gf];\n", " out_gf = metric_gfs_face_list[gf];\n", " for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) {\n", " for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) {\n", " for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) {\n", " Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])];\n", " Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])];\n", " Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)];\n", " Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])];\n", " auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1);\n", " }\n", " }\n", " }\n", " }\n", "#ifdef WORKAROUND_ENABLED\n", " for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) {\n", " for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) {\n", " for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) {\n", " Qm2 = auxevol_gfs[IDX4S(PHIGF,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])];\n", " Qm1 = auxevol_gfs[IDX4S(PHIGF,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])];\n", " Qp0 = auxevol_gfs[IDX4S(PHIGF,i0,i1,i2)];\n", " Qp1 = auxevol_gfs[IDX4S(PHIGF,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])];\n", " auxevol_gfs[IDX4S(PSI6_TEMPGF,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1);\n", " }\n", " }\n", " }\n", "#endif /*WORKAROUND_ENABLED*/\n", "\n", "\"\"\",\n", " rel_path_to_Cparams=os.path.join(\"../\"))\n", "\n", "with open(os.path.join(Ccodesdir,\"interpolate_metric_gfs_to_cell_faces.h\"),\"a\") as file:\n", " file.write(interp_Cfunc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 2: Code Validation against `GiRaFFE_NRPy_FCVAL.py` \\[Back to [top](#toc)\\]\n", "$$\\label{code_validation}$$\n", "\n", "Now, we will confirm that the code we have written here is the same as that generated by the module [`GiRaFFE_NRPy_FCVAL.py`](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_FCVAL.py)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Printing difference between original C code and this code...\n", "Checking file interpolate_metric_gfs_to_cell_faces.h\n", "No difference. TEST PASSED!\n" ] } ], "source": [ "# Define the directory that we wish to validate against:\n", "valdir = \"GiRaFFE_NRPy/GiRaFFE_Ccode_library/FCVAL/\"\n", "\n", "import GiRaFFE_NRPy.GiRaFFE_NRPy_Metric_Face_Values as FCVAL\n", "FCVAL.GiRaFFE_NRPy_FCVAL(valdir)\n", "\n", "import difflib\n", "import sys\n", "\n", "print(\"Printing difference between original C code and this code...\")\n", "# Open the files to compare\n", "file = \"interpolate_metric_gfs_to_cell_faces.h\"\n", "\n", "print(\"Checking file \" + file)\n", "with open(os.path.join(valdir,file)) as file1, open(os.path.join(Ccodesdir,file)) as file2:\n", " # Read the lines of each file\n", " file1_lines = file1.readlines()\n", " file2_lines = file2.readlines()\n", " num_diffs = 0\n", " for line in difflib.unified_diff(file1_lines, file2_lines, fromfile=os.path.join(valdir+file), tofile=os.path.join(Ccodesdir+file)):\n", " sys.stdout.writelines(line)\n", " num_diffs = num_diffs + 1\n", " if num_diffs == 0:\n", " print(\"No difference. TEST PASSED!\")\n", " else:\n", " print(\"ERROR: Disagreement found with .py file. See differences above.\")\n", " sys.exit(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 3: Output this notebook to $\\LaTeX$-formatted PDF file \\[Back to [top](#toc)\\]\n", "$$\\label{latex_pdf_output}$$\n", "\n", "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\n", "[Tutorial-GiRaFFE_NRPy-FCVAL.pdf](Tutorial-GiRaFFE_NRPy-FCVAL.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pdflatex: security risk: running with elevated privileges\n", "pdflatex: security risk: running with elevated privileges\n", "pdflatex: security risk: running with elevated privileges\n", "Created Tutorial-GiRaFFE_NRPy-Metric_Face_Values.tex, and compiled LaTeX\n", " file to PDF file Tutorial-GiRaFFE_NRPy-Metric_Face_Values.pdf\n" ] } ], "source": [ "import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n", "cmd.output_Jupyter_notebook_to_LaTeXed_PDF(\"Tutorial-GiRaFFE_NRPy-Metric_Face_Values\",location_of_template_file=os.path.join(\"..\"))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 2 }