{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"\n",
"# Tetrads for Evaluating the Outgoing Gravitational Wave Weyl scalar $\\psi_4$\n",
"\n",
"## Authors: Patrick Nelson & Zach Etienne\n",
"\n",
"## This tutorial demonstrates the construction of quasi-Kinnersley tetrads for the Weyl scalar $\\Psi_4$. It details the process of initializing core NRPy+ modules, setting the numerical grid's coordinate system, defining vectors, and orthogonalizing them using the Levi-Civita symbol.\n",
"\n",
"**Notebook Status:** Validated \n",
"\n",
"**Validation Notes:** See [$\\psi_4$ notebook](Tutorial-Psi4.ipynb), whose Python module depends on the one presented here, for all validation notes.\n",
"\n",
"### NRPy+ Source Code for this module: [BSSN/Psi4_tetrads.py](../edit/BSSN/Psi4_tetrads.py)\n",
"\n",
"## Introduction: \n",
"This module constructs tetrad vectors $l^\\mu$, $m^\\mu$, and $n^\\mu$ for the $\\psi_4$ Weyl scalar, a quantity that is immensely useful when extracting gravitational wave content from a numerical relativity simulation. $\\psi_4$ is related to the gravitational wave strain via\n",
"\n",
"$$\n",
"\\psi_4 = \\ddot{h}_+ - i \\ddot{h}_\\times.\n",
"$$\n",
"\n",
"We construct $\\psi_4$ from the standard ADM spatial metric $\\gamma_{ij}$ and extrinsic curvature $K_{ij}$, and their derivatives. The full expression is given by Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf):\n",
"\n",
"\\begin{align}\n",
"\\psi_4 &= \\left[ {R}_{ijkl}+2K_{i[k}K_{l]j}\\right]\n",
"{n}^i\\bar{m}^j{n}^k\\bar{m}^l \\\\\n",
"& -8\\left[ K_{j[k,l]}+{\\Gamma }_{j[k}^pK_{l]p}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^k\\bar{m}^l \\\\\n",
"& +4\\left[ {R}_{jl}-K_{jp}K_l^p+KK_{jl}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^{[0}\\bar{m}^{l]},\n",
"\\end{align}\n",
"\n",
"Note that $\\psi_4$ is complex, with the imaginary components originating from the tetrad vector $m^\\mu$. This module does not specify a tetrad; instead, it only constructs the above expression leaving $m^\\mu$ and $n^\\mu$ unspecified. This module defines these tetrad quantities, implementing the quasi-Kinnersley tetrad of [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), also referred to as \"***the BCL paper***\".\n",
"\n",
"### A Note on Notation:\n",
"\n",
"As is standard in NRPy+, \n",
"\n",
"* Greek indices range from 0 to 3, inclusive, with the zeroth component denoting the temporal (time) component.\n",
"* Latin indices range from 0 to 2, inclusive, with the zeroth component denoting the first spatial component.\n",
"\n",
"As a corollary, any expressions involving mixed Greek and Latin indices will need to offset one set of indices by one: A Latin index in a four-vector will be incremented and a Greek index in a three-vector will be decremented (however, the latter case does not occur in this tutorial notebook)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"# Table of Contents\n",
"$$\\label{toc}$$\n",
"\n",
"This tutorial notebook is organized as follows\n",
"\n",
"1. [Step 1](#initializenrpy): Initialize needed NRPy+ modules\n",
"1. [Step 2](#quasikinnersley): The quasi-Kinnersley tetrad\n",
"1. [Step 3](#code_validation): Code Validation against `BSSN.Psi4_tetrads` NRPy+ module\n",
"1. [Step 4](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"# Step 1: Initialize core NRPy+ modules \\[Back to [top](#toc)\\]\n",
"$$\\label{initializenrpy}$$"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:52.011631Z",
"iopub.status.busy": "2021-03-07T17:15:52.010360Z",
"iopub.status.idle": "2021-03-07T17:15:53.140162Z",
"shell.execute_reply": "2021-03-07T17:15:53.140763Z"
}
},
"outputs": [],
"source": [
"# Step 1.a: import all needed modules from NRPy+:\n",
"import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends\n",
"import NRPy_param_funcs as par # NRPy+: Parameter interface\n",
"import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support\n",
"import reference_metric as rfm # NRPy+: Reference metric support\n",
"import sys # Standard Python modules for multiplatform OS-level functions\n",
"\n",
"# Step 1.b: Set the coordinate system for the numerical grid\n",
"par.set_parval_from_str(\"reference_metric::CoordSystem\",\"Spherical\")\n",
"\n",
"# Step 1.c: Given the chosen coordinate system, set up\n",
"# corresponding reference metric and needed\n",
"# reference metric quantities\n",
"# The following function call sets up the reference metric\n",
"# and related quantities, including rescaling matrices ReDD,\n",
"# ReU, and hatted quantities.\n",
"rfm.reference_metric()\n",
"\n",
"# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is\n",
"# a 3+1-dimensional decomposition of the general\n",
"# relativistic field equations)\n",
"DIM = 3\n",
"\n",
"# Step 1.e: Import all ADM quantities as written in terms of BSSN quantities\n",
"import BSSN.ADM_in_terms_of_BSSN as AB\n",
"AB.ADM_in_terms_of_BSSN()\n",
"\n",
"# Step 1.f: Initialize TetradChoice parameter\n",
"thismodule = __name__\n",
"# Current option: QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)\n",
"par.initialize_param(par.glb_param(\"char\", thismodule, \"TetradChoice\", \"QuasiKinnersley\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"# Step 2: The quasi-Kinnersley tetrad of [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf) \\[Back to [top](#toc)\\]\n",
"$$\\label{quasikinnersley}$$\n",
"\n",
"To define the Weyl scalars, first, a tetrad must be chosen. Below, for compatibility with the [WeylScal4 diagnostic module](https://bitbucket.org/einsteintoolkit/einsteinanalysis/src/master/WeylScal4/), we implement the quasi-Kinnersley tetrad of [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf).\n",
"\n",
"We begin with the vectors given in eqs. 5.6 and 5.7 of the BCL paper, which are orthogonal to each other in flat spacetime; one is in the $\\phi$ direction, one is in $r$, and the third is the cross product of the first two:\n",
"\\begin{align}\n",
" v_1^a &= [-y,x,0] \\\\\n",
" v_2^a &= [x,y,z] \\\\\n",
" v_3^a &= {\\rm det}(\\gamma)^{1/2} \\gamma^{ad} \\epsilon_{dbc} v_1^b v_2^c,\n",
"\\end{align}\n",
"\n",
"Notice that $v_1^a$ and $v_2^a$ assume the Cartesian basis, but $\\gamma^{ad}$ will be in the $xx^i$ basis given by the chosen `reference_metric::CoordSystem`. Thus to construct $v_3^a$, we must first perform a change of basis on $v_1^a$ and $v_2^a$:\n",
"\n",
"$$\n",
"v_{1,{\\rm xx}}^a = \\frac{\\partial xx^a}{\\partial x_{\\rm Cart}^b} v_{1,{\\rm Cart}}^b.\n",
"$$\n",
"This equation is problematic because we generally do not have a closed-form expression for components of the $xx^a$ vector as functions of the Cartesian coordinate vector components $x_{\\rm Cart}^a$. However, we do have closed-form expressions for components of $x_{\\rm Cart}^a$ as functions of $xx^a$. Thus we can construct the needed Jacobian matrix $\\frac{\\partial xx^a}{\\partial x_{\\rm Cart}^b}$ by evaluating the derivative $\\frac{\\partial x_{\\rm Cart}^b}{\\partial xx^a}$ and performing a simple matrix inversion:\n",
"$$\n",
"\\frac{\\partial xx^a}{\\partial x_{\\rm Cart}^b} = \\left(\\frac{\\partial x_{\\rm Cart}^b}{\\partial xx^a} \\right)^{-1}.\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:53.215312Z",
"iopub.status.busy": "2021-03-07T17:15:53.179543Z",
"iopub.status.idle": "2021-03-07T17:15:55.668569Z",
"shell.execute_reply": "2021-03-07T17:15:55.669121Z"
}
},
"outputs": [],
"source": [
"# Step 2.a: Declare the Cartesian x,y,z in terms of\n",
"# xx0,xx1,xx2.\n",
"x = rfm.xx_to_Cart[0]\n",
"y = rfm.xx_to_Cart[1]\n",
"z = rfm.xx_to_Cart[2]\n",
"\n",
"# Step 2.b: Declare detgamma and gammaUU from\n",
"# BSSN.ADM_in_terms_of_BSSN;\n",
"# simplify detgamma & gammaUU expressions,\n",
"# which expedites Psi4 codegen.\n",
"detgamma = sp.simplify(AB.detgamma)\n",
"gammaUU = ixp.zerorank2()\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" gammaUU[i][j] = sp.simplify(AB.gammaUU[i][j])\n",
"\n",
"# Step 2.c: Define v1U and v2U\n",
"v1UCart = [-y, x, sp.sympify(0)]\n",
"v2UCart = [x, y, z]\n",
"\n",
"# Step 2.d: Construct the Jacobian d x_Cart^i / d xx^j\n",
"Jac_dUCart_dDrfmUD = ixp.zerorank2()\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" Jac_dUCart_dDrfmUD[i][j] = sp.simplify(sp.diff(rfm.xx_to_Cart[i], rfm.xx[j]))\n",
"\n",
"# Step 2.e: Invert above Jacobian to get needed d xx^j / d x_Cart^i\n",
"Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUCart_dDrfmUD)\n",
"\n",
"# Step 2.e.i: Simplify expressions for d xx^j / d x_Cart^i:\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" Jac_dUrfm_dDCartUD[i][j] = sp.simplify(Jac_dUrfm_dDCartUD[i][j])\n",
"\n",
"# Step 2.f: Transform v1U and v2U from the Cartesian to the xx^i basis\n",
"v1U = ixp.zerorank1()\n",
"v2U = ixp.zerorank1()\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j]\n",
" v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... next we construct the third tetrad vector $v_3^a={\\rm det}(\\gamma)^{1/2} \\gamma^{ad} \\epsilon_{dbc} v_1^b v_2^c$, using the Levi-Civita symbol $\\epsilon_{dbc}$ as defined in [indexedexp.py](../edit/indexedexp.py):"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:55.708381Z",
"iopub.status.busy": "2021-03-07T17:15:55.702757Z",
"iopub.status.idle": "2021-03-07T17:15:55.711174Z",
"shell.execute_reply": "2021-03-07T17:15:55.710501Z"
}
},
"outputs": [],
"source": [
"# Step 2.g: Define v3U\n",
"v3U = ixp.zerorank1()\n",
"LeviCivitaSymbolDDD = ixp.LeviCivitaSymbol_dim3_rank3()\n",
"for a in range(DIM):\n",
" for b in range(DIM):\n",
" for c in range(DIM):\n",
" for d in range(DIM):\n",
" v3U[a] += sp.sqrt(detgamma)*gammaUU[a][d]*LeviCivitaSymbolDDD[d][b][c]*v1U[b]*v2U[c]\n",
"\n",
"# Step 2.g.i: Simplify expressions for v1U,v2U,v3U. This greatly expedites the C code generation (~10x faster)\n",
"# Drat. Simplification with certain versions of SymPy & coord systems results in a hang. Let's just\n",
"# evaluate the expressions so the most trivial optimizations can be performed.\n",
"for a in range(DIM):\n",
" v1U[a] = v1U[a].doit() #sp.simplify(v1U[a])\n",
" v2U[a] = v2U[a].doit() #sp.simplify(v2U[a])\n",
" v3U[a] = v3U[a].doit() #sp.simplify(v3U[a])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As our next step, we carry out the Gram-Schmidt orthonormalization process. The vectors $v_i^a$ are placeholders in the code; the final product of the orthonormalization is the vectors $e_i^a$. So,\n",
"\\begin{align}\n",
"e_1^a &= \\frac{v_1^a}{\\sqrt{\\omega_{11}}} \\\\\n",
"e_2^a &= \\frac{v_2^a - \\omega_{12} e_1^a}{\\sqrt{\\omega_{22}}} \\\\\n",
"e_3^a &= \\frac{v_3^a - \\omega_{13} e_1^a - \\omega_{23} e_2^a}{\\sqrt{\\omega_{33}}}, \\text{ where}\\\\\n",
"\\omega_{ij} &= v_i^a v_j^b \\gamma_{ab}\n",
"\\end{align}\n",
"\n",
"Note that the above expressions must be evaluated with the numerators first so that the denominators generate the proper normalization."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:55.765227Z",
"iopub.status.busy": "2021-03-07T17:15:55.729307Z",
"iopub.status.idle": "2021-03-07T17:15:55.808247Z",
"shell.execute_reply": "2021-03-07T17:15:55.807516Z"
}
},
"outputs": [],
"source": [
"# Step 2.h: Define omega_{ij}\n",
"omegaDD = ixp.zerorank2()\n",
"gammaDD = AB.gammaDD\n",
"def v_vectorDU(v1U,v2U,v3U, i,a):\n",
" if i==0:\n",
" return v1U[a]\n",
" if i==1:\n",
" return v2U[a]\n",
" if i==2:\n",
" return v3U[a]\n",
" print(\"ERROR: unknown vector!\")\n",
" sys.exit(1)\n",
"\n",
"def update_omega(omegaDD, i,j, v1U,v2U,v3U,gammaDD):\n",
" omegaDD[i][j] = sp.sympify(0)\n",
" for a in range(DIM):\n",
" for b in range(DIM):\n",
" omegaDD[i][j] += v_vectorDU(v1U,v2U,v3U, i,a)*v_vectorDU(v1U,v2U,v3U, j,b)*gammaDD[a][b]\n",
"\n",
"# Step 2.i: Define e^a_i. Note that:\n",
"# omegaDD[0][0] = \\omega_{11} above;\n",
"# omegaDD[1][1] = \\omega_{22} above, etc.\n",
"e1U = ixp.zerorank1()\n",
"e2U = ixp.zerorank1()\n",
"e3U = ixp.zerorank1()\n",
"# First e_1^a: Orthogonalize & normalize:\n",
"update_omega(omegaDD, 0,0, v1U,v2U,v3U,gammaDD)\n",
"for a in range(DIM):\n",
" e1U[a] = v1U[a]/sp.sqrt(omegaDD[0][0])\n",
"\n",
"# Next e_2^a: First orthogonalize:\n",
"update_omega(omegaDD, 0,1, e1U,v2U,v3U,gammaDD)\n",
"for a in range(DIM):\n",
" e2U[a] = (v2U[a] - omegaDD[0][1]*e1U[a])\n",
"# Then normalize:\n",
"update_omega(omegaDD, 1,1, e1U,e2U,v3U,gammaDD)\n",
"for a in range(DIM):\n",
" e2U[a] /= sp.sqrt(omegaDD[1][1])\n",
"\n",
"# Next e_3^a: First orthogonalize:\n",
"update_omega(omegaDD, 0,2, e1U,e2U,v3U,gammaDD)\n",
"update_omega(omegaDD, 1,2, e1U,e2U,v3U,gammaDD)\n",
"for a in range(DIM):\n",
" e3U[a] = (v3U[a] - omegaDD[0][2]*e1U[a] - omegaDD[1][2]*e2U[a])\n",
"# Then normalize:\n",
"update_omega(omegaDD, 2,2, e1U,e2U,e3U,gammaDD)\n",
"for a in range(DIM):\n",
" e3U[a] /= sp.sqrt(omegaDD[2][2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Once we have orthogonal, normalized vectors, we can construct the tetrad itself, again drawing on eqs. 5.6. We can draw on SymPy's built-in tools for complex numbers to build the complex vector $m^a$:\n",
"\\begin{align}\n",
" l^\\mu &= \\frac{1}{\\sqrt{2}} \\left(u^\\mu + r^\\mu\\right) \\\\\n",
" n^\\mu &= \\frac{1}{\\sqrt{2}} \\left(u^\\mu - r^\\mu\\right) \\\\\n",
" \\Re(m^\\mu) &= \\frac{1}{\\sqrt{2}} \\theta^\\mu \\\\\n",
" \\Im(m^\\mu) &= \\frac{1}{\\sqrt{2}} \\phi^\\mu,\n",
"\\end{align}\n",
"where $r^\\mu=\\{0,e_2^i\\}$, $\\theta^\\mu=\\{0,e_3^i\\}$, $\\phi^\\mu=\\{0,e_1^i\\}$, and $u^\\mu$ is the time-like unit normal to the hypersurface."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:55.820257Z",
"iopub.status.busy": "2021-03-07T17:15:55.819468Z",
"iopub.status.idle": "2021-03-07T17:15:55.821831Z",
"shell.execute_reply": "2021-03-07T17:15:55.822345Z"
},
"scrolled": true
},
"outputs": [],
"source": [
"# Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:\n",
"r4U = ixp.zerorank1(DIM=4)\n",
"u4U = ixp.zerorank1(DIM=4)\n",
"theta4U = ixp.zerorank1(DIM=4)\n",
"phi4U = ixp.zerorank1(DIM=4)\n",
"\n",
"for a in range(DIM):\n",
" r4U[ a+1] = e2U[a]\n",
" theta4U[a+1] = e3U[a]\n",
" phi4U[ a+1] = e1U[a]\n",
"\n",
"# FIXME? assumes alpha=1, beta^i = 0\n",
"u4U[0] = 1\n",
"\n",
"l4U = ixp.zerorank1(DIM=4)\n",
"n4U = ixp.zerorank1(DIM=4)\n",
"mre4U = ixp.zerorank1(DIM=4)\n",
"mim4U = ixp.zerorank1(DIM=4)\n",
"\n",
"# M_SQRT1_2 = 1 / sqrt(2) (defined in math.h on Linux)\n",
"M_SQRT1_2 = par.Cparameters(\"#define\",thismodule,\"M_SQRT1_2\",\"\")\n",
"isqrt2 = M_SQRT1_2 #1/sp.sqrt(2) <- SymPy drops precision to 15 sig. digits in unit tests\n",
"for mu in range(4):\n",
" l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])\n",
" n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])\n",
" mre4U[mu] = isqrt2*theta4U[mu]\n",
" mim4U[mu] = isqrt2* phi4U[mu]\n",
"\n",
"# ltetU,ntetU,remtetU,immtetU,e1U,e2U,e3U\n",
"for mu in range(4):\n",
" l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])\n",
" n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])\n",
" mre4U[mu] = isqrt2*theta4U[mu]\n",
" mim4U[mu] = isqrt2* phi4U[mu]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"# Step 3: Code validation against `BSSN.Psi4_tetrads` NRPy+ module \\[Back to [top](#toc)\\]\n",
"$$\\label{code_validation}$$\n",
"\n",
"As a code validation check, we verify agreement in the SymPy expressions for the RHSs of the BSSN equations between\n",
"1. this tutorial and \n",
"2. the NRPy+ [BSSN.Psi4_tetrads](../edit/BSSN/Psi4_tetrads.py) module.\n",
"\n",
"By default, we compare all quantities in Spherical coordinates, though other coordinate systems may be chosen."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:55.833390Z",
"iopub.status.busy": "2021-03-07T17:15:55.832620Z",
"iopub.status.idle": "2021-03-07T17:15:58.329594Z",
"shell.execute_reply": "2021-03-07T17:15:58.330100Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ALL TESTS PASSED!\n"
]
}
],
"source": [
"def comp_func(expr1,expr2,basename,prefixname2=\"BP4T.\"):\n",
" if str(expr1-expr2)!=\"0\":\n",
" print(basename+\" - \"+prefixname2+basename+\" = \"+ str(expr1-expr2))\n",
" return 1\n",
" return 0\n",
"\n",
"def gfnm(basename,idx1,idx2=None,idx3=None):\n",
" if idx2 is None:\n",
" return basename+\"[\"+str(idx1)+\"]\"\n",
" if idx3 is None:\n",
" return basename+\"[\"+str(idx1)+\"][\"+str(idx2)+\"]\"\n",
" return basename+\"[\"+str(idx1)+\"][\"+str(idx2)+\"][\"+str(idx3)+\"]\"\n",
"\n",
"expr_list = []\n",
"exprcheck_list = []\n",
"namecheck_list = []\n",
"\n",
"import BSSN.Psi4_tetrads as BP4T\n",
"BP4T.Psi4_tetrads()\n",
"\n",
"for mu in range(4):\n",
" namecheck_list.extend([gfnm(\"l4U\",mu),gfnm(\"n4U\",mu),gfnm(\"mre4U\",mu),gfnm(\"mim4U\",mu)])\n",
" exprcheck_list.extend([BP4T.l4U[mu],BP4T.n4U[mu],BP4T.mre4U[mu],BP4T.mim4U[mu]])\n",
" expr_list.extend([l4U[mu],n4U[mu],mre4U[mu],mim4U[mu]])\n",
"\n",
"num_failures = 0\n",
"for i in range(len(expr_list)):\n",
" num_failures += comp_func(expr_list[i],exprcheck_list[i],namecheck_list[i])\n",
"\n",
"import sys\n",
"if num_failures == 0:\n",
" print(\"ALL TESTS PASSED!\")\n",
"else:\n",
" print(\"ERROR: \" + str(num_failures) + \" TESTS DID NOT PASS\")\n",
" sys.exit(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"# Step 4: 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-Psi4_tetrads.pdf](Tutorial-Psi4_tetrads.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": 1,
"metadata": {
"execution": {
"iopub.execute_input": "2021-03-07T17:15:58.335326Z",
"iopub.status.busy": "2021-03-07T17:15:58.334227Z",
"iopub.status.idle": "2021-03-07T17:16:01.828438Z",
"shell.execute_reply": "2021-03-07T17:16:01.829186Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Created Tutorial-Psi4_tetrads.tex, and compiled LaTeX file to PDF file\n",
" Tutorial-Psi4_tetrads.pdf\n"
]
}
],
"source": [
"import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n",
"cmd.output_Jupyter_notebook_to_LaTeXed_PDF(\"Tutorial-Psi4_tetrads\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}