{ "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 }