{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "# BSSN Time-Evolution Equations for the Gauge Fields $\\alpha$ and $\\beta^i$\n", "\n", "## Authors: Zach Etienne & Terrence Pierre Jacques\n", "### Formatting improvements courtesy Brandon Clark\n", "\n", "[comment]: <> (Abstract: TODO, or make the introduction an abstract and additional notes section, and write a new Introduction)\n", "\n", "## This notebook constructs SymPy expressions for the right-hand sides of the time-evolution equations for the gauge fields $\\alpha$ (the lapse) and $\\beta^i$ (the shift) in the BSSN formalism. The tutorial explores various gauge conditions and their robustness in the presence of black holes. Various lapse and shift conditions, including the $1+\\log$ lapse, harmonic slicing, frozen lapse, and gamma-driving shift conditions, are elaborated.\n", "\n", "**Notebook Status:** Validated \n", "\n", "**Validation Notes:** All expressions generated in this module have been validated against a trusted code (the original NRPy+/SENR code, which itself was validated against [Baumgarte's code](https://arxiv.org/abs/1211.6632)).\n", "\n", "### NRPy+ Source Code for this module: [BSSN/BSSN_gauge_RHSs.py](../edit/BSSN/BSSN_gauge_RHSs.py)\n", "\n", "\n", "## Introduction:\n", "This tutorial demonstrates the generation of SymPy expressions for the time-evolution equations concerning the gauge fields $\\alpha$ and $\\beta^i$, fundamental components in the 3+1 solution to Einstein’s equations. $\\alpha$, or the lapse, dictates the amount of proper time elapsing between one timestep to the next at each point, while $\\beta^i$, known as the shift, determines the proper distance numerical grid points travel from one timestep to the next. Although there are no strict requirements for the selection of gauge conditions, few have shown robustness in scenarios with black holes, hence the focus of this notebook is limited to the most stable selections." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Table of Contents\n", "$$\\label{toc}$$\n", "\n", "This notebook is organized as follows\n", "\n", "1. [Step 1](#initializenrpy): Initialize needed Python/NRPy+ modules\n", "1. [Step 2](#lapseconditions): Right-hand side of $\\partial_t \\alpha$\n", " 1. [Step 2.a](#onepluslog): $1+\\log$ lapse\n", " 1. [Step 2.b](#harmonicslicing): Harmonic slicing\n", " 1. [Step 2.c](#frozen): Frozen lapse\n", " 1. [Step 2.d](#statictrumpet_onepluslog): Alternative 1+log condition for Static Trumpet initial data\n", "1. [Step 3](#shiftconditions): Right-hand side of $\\partial_t \\beta^i$: Second-order Gamma-driving shift conditions\n", " 1. [Step 3.a](#origgammadriving): Original, non-covariant Gamma-driving shift condition\n", " 1. [Step 3.b](#covgammadriving): [Brown](https://arxiv.org/abs/0902.3652)'s suggested covariant Gamma-driving shift condition\n", " 1. [Step 3.b.i](#partial_beta): The right-hand side of the $\\partial_t \\beta^i$ equation\n", " 1. [Step 3.b.ii](#partial_upper_b): The right-hand side of the $\\partial_t B^i$ equation\n", " 1. [Step 3.c](#statictrumpet_nonadvecgammadriving): Non-advecting Gamma-driving shift condition (used for evolving \"Static Trumpet\" initial data)\n", "1. [Step 4](#rescale): Rescale right-hand sides of BSSN gauge equations\n", "1. [Step 5](#code_validation): Code Validation against `BSSN.BSSN_gauge_RHSs` NRPy+ module\n", "1. [Step 6](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 1: Initialize needed Python/NRPy+ modules \\[Back to [top](#toc)\\]\n", "$$\\label{initializenrpy}$$\n", "\n", "Let's start by importing all the needed modules from Python/NRPy+:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:55.800214Z", "iopub.status.busy": "2021-03-07T17:21:55.799384Z", "iopub.status.idle": "2021-03-07T17:21:58.416995Z", "shell.execute_reply": "2021-03-07T17:21:58.416408Z" } }, "outputs": [], "source": [ "# Step 1: 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 grid as gri # NRPy+: Functions having to do with numerical grids\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 BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities\n", "import BSSN.BSSN_RHSs as Brhs # NRPy+: Constructs BSSN right-hand-side expressions\n", "import sys # Standard Python modules for multiplatform OS-level functions\n", "\n", "# Step 1.c: Declare/initialize parameters for this module\n", "thismodule = \"BSSN_gauge_RHSs\"\n", "par.initialize_param(par.glb_param(\"char\", thismodule, \"LapseEvolutionOption\", \"OnePlusLog\"))\n", "par.initialize_param(par.glb_param(\"char\", thismodule, \"ShiftEvolutionOption\", \"GammaDriving2ndOrder_Covariant\"))\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: 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.f: Define BSSN scalars & tensors (in terms of rescaled BSSN quantities)\n", "import BSSN.BSSN_quantities as Bq\n", "Bq.BSSN_basic_tensors()\n", "Bq.betaU_derivs()\n", "\n", "import BSSN.BSSN_RHSs as Brhs\n", "Brhs.BSSN_RHSs()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 2: Right-hand side of $\\partial_t \\alpha$ \\[Back to [top](#toc)\\]\n", "$$\\label{lapseconditions}$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 2.a: $1+\\log$ lapse \\[Back to [top](#toc)\\]\n", "$$\\label{onepluslog}$$\n", "\n", "The [$1=\\log$ lapse condition](https://arxiv.org/abs/gr-qc/0206072) is a member of the [Bona-Masso family of lapse choices](https://arxiv.org/abs/gr-qc/9412071), which has the desirable property of singularity avoidance. As is common (e.g., see [Campanelli *et al* (2005)](https://arxiv.org/abs/gr-qc/0511048)), we make the replacement $\\partial_t \\to \\partial_0 = \\partial_t - \\beta^i \\partial_i$ to ensure lapse characteristics advect with the shift. The bracketed term in the $1+\\log$ lapse condition below encodes the shift advection term:\n", "\n", "\\begin{align}\n", "\\partial_0 \\alpha &= -2 \\alpha K \\\\\n", "\\implies \\partial_t \\alpha &= \\left[\\beta^i \\partial_i \\alpha\\right] - 2 \\alpha K\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.425515Z", "iopub.status.busy": "2021-03-07T17:21:58.424823Z", "iopub.status.idle": "2021-03-07T17:21:58.427465Z", "shell.execute_reply": "2021-03-07T17:21:58.426931Z" } }, "outputs": [], "source": [ "# Step 2.a: The 1+log lapse condition:\n", "# \\partial_t \\alpha = \\beta^i \\alpha_{,i} - 2*\\alpha*K\n", "# First import expressions from BSSN_quantities\n", "cf = Bq.cf\n", "trK = Bq.trK\n", "alpha = Bq.alpha\n", "betaU = Bq.betaU\n", "\n", "# Implement the 1+log lapse condition\n", "if par.parval_from_str(thismodule+\"::LapseEvolutionOption\") == \"OnePlusLog\":\n", " alpha_rhs = -2*alpha*trK\n", " alpha_dupD = ixp.declarerank1(\"alpha_dupD\")\n", " for i in range(DIM):\n", " alpha_rhs += betaU[i]*alpha_dupD[i]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 2.b: Harmonic slicing \\[Back to [top](#toc)\\]\n", "$$\\label{harmonicslicing}$$\n", "\n", "As defined on Pg 2 of https://arxiv.org/pdf/gr-qc/9902024.pdf , this is given by \n", "\n", "$$\n", "\\partial_t \\alpha = \\partial_t e^{6 \\phi} = 6 e^{6 \\phi} \\partial_t \\phi\n", "$$\n", "\n", "If \n", "\n", "$$\\text{cf} = W = e^{-2 \\phi},$$ \n", "\n", "then\n", "\n", "$$\n", "6 e^{6 \\phi} \\partial_t \\phi = 6 W^{-3} \\partial_t \\phi.\n", "$$\n", "\n", "However,\n", "$$\n", "\\partial_t \\phi = -\\partial_t \\text{cf} / (2 \\text{cf})$$\n", "\n", "(as described above), so if `cf`$=W$, then\n", "\\begin{align}\n", "\\partial_t \\alpha &= 6 e^{6 \\phi} \\partial_t \\phi \\\\\n", "&= 6 W^{-3} \\left(-\\frac{\\partial_t W}{2 W}\\right) \\\\\n", "&= -3 \\text{cf}^{-4} \\text{cf}\\_\\text{rhs}\n", "\\end{align}\n", "\n", "**Exercise to students: Implement Harmonic slicing for `cf`$=\\chi$** " ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.433453Z", "iopub.status.busy": "2021-03-07T17:21:58.432662Z", "iopub.status.idle": "2021-03-07T17:21:58.434925Z", "shell.execute_reply": "2021-03-07T17:21:58.435395Z" } }, "outputs": [], "source": [ "# Step 2.b: Implement the harmonic slicing lapse condition\n", "if par.parval_from_str(thismodule+\"::LapseEvolutionOption\") == \"HarmonicSlicing\":\n", " if par.parval_from_str(\"BSSN.BSSN_quantities::EvolvedConformalFactor_cf\") == \"W\":\n", " alpha_rhs = -3*cf**(-4)*Brhs.cf_rhs\n", " elif par.parval_from_str(\"BSSN.BSSN_quantities::EvolvedConformalFactor_cf\") == \"phi\":\n", " alpha_rhs = 6*sp.exp(6*cf)*Brhs.cf_rhs\n", " else:\n", " print(\"Error LapseEvolutionOption==HarmonicSlicing unsupported for EvolvedConformalFactor_cf!=(W or phi)\")\n", " sys.exit(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 2.c: Frozen lapse \\[Back to [top](#toc)\\]\n", "$$\\label{frozen}$$\n", "\n", "This slicing condition is given by\n", "$$\\partial_t \\alpha = 0,$$\n", "\n", "which is rarely a stable lapse condition." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.440683Z", "iopub.status.busy": "2021-03-07T17:21:58.439922Z", "iopub.status.idle": "2021-03-07T17:21:58.442993Z", "shell.execute_reply": "2021-03-07T17:21:58.443943Z" } }, "outputs": [], "source": [ "# Step 2.c: Frozen lapse\n", "# \\partial_t \\alpha = 0\n", "if par.parval_from_str(thismodule+\"::LapseEvolutionOption\") == \"Frozen\":\n", " alpha_rhs = sp.sympify(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 2.d: Alternative $1+\\log$ lapse for Static Trumpet initial data \\[Back to [top](#toc)\\]\n", "$$\\label{statictrumpet_onepluslog}$$\n", "\n", "An alternative to the standard 1+log condition to be used with Static Trumpet initial data, the lapse is evolved according to a\n", "condition consistent with staticity, given by equation 67 in [Ruchlin, Etienne, & Baumgarte (2018)](https://arxiv.org/pdf/1712.07658.pdf)\n", "\n", "\\begin{align}\n", "\\partial_0 \\alpha &= -\\alpha(1 - \\alpha) K \\\\\n", "\\implies \\partial_t \\alpha &= \\left[\\beta^i \\partial_i \\alpha\\right] -\\alpha(1 - \\alpha) K\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.452895Z", "iopub.status.busy": "2021-03-07T17:21:58.452086Z", "iopub.status.idle": "2021-03-07T17:21:58.454568Z", "shell.execute_reply": "2021-03-07T17:21:58.455119Z" } }, "outputs": [], "source": [ "# Step 2.d: Alternative 1+log lapse condition:\n", "# \\partial_t \\alpha = \\beta^i \\alpha_{,i} -\\alpha*(1 - \\alpha)*K\n", "\n", "# Implement the alternative 1+log lapse condition\n", "if par.parval_from_str(thismodule+\"::LapseEvolutionOption\") == \"OnePlusLogAlt\":\n", " alpha_rhs = -alpha*(1 - alpha)*trK\n", " alpha_dupD = ixp.declarerank1(\"alpha_dupD\")\n", " for i in range(DIM):\n", " alpha_rhs += betaU[i]*alpha_dupD[i]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 3: Right-hand side of $\\partial_t \\beta^i$: Second-order Gamma-driving shift conditions \\[Back to [top](#toc)\\]\n", "$$\\label{shiftconditions}$$\n", "\n", "The motivation behind Gamma-driving shift conditions is well documented in the book [*Numerical Relativity* by Baumgarte & Shapiro](https://www.amazon.com/Numerical-Relativity-Einsteins-Equations-Computer/dp/052151407X/).\n", "\n", "\n", "\n", "## Step 3.a: Original, non-covariant Gamma-driving shift condition \\[Back to [top](#toc)\\]\n", "$$\\label{origgammadriving}$$\n", "\n", "**Option 1: Non-Covariant, Second-Order Shift**\n", "\n", "We adopt the [*shifting (i.e., advecting) shift*](https://arxiv.org/abs/gr-qc/0605030) non-covariant, second-order shift condition:\n", "\\begin{align}\n", "\\partial_0 \\beta^i &= B^{i} \\\\\n", "\\partial_0 B^i &= \\frac{3}{4} \\partial_{0} \\bar{\\Lambda}^{i} - \\eta B^{i} \\\\\n", "\\implies \\partial_t \\beta^i &= \\left[\\beta^j \\partial_j \\beta^i\\right] + B^{i} \\\\\n", "\\partial_t B^i &= \\left[\\beta^j \\partial_j B^i\\right] + \\frac{3}{4} \\partial_{0} \\bar{\\Lambda}^{i} - \\eta B^{i},\n", "\\end{align}\n", "where $\\eta$ is the shift damping parameter, and $\\partial_{0} \\bar{\\Lambda}^{i}$ in the right-hand side of the $\\partial_{0} B^{i}$ equation is computed by adding $\\beta^j \\partial_j \\bar{\\Lambda}^i$ to the right-hand side expression given for $\\partial_t \\bar{\\Lambda}^i$ in the BSSN time-evolution equations as listed [here](Tutorial-BSSN_formulation.ipynb), so no explicit time dependence occurs in the right-hand sides of the BSSN evolution equations and the Method of Lines can be applied directly." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.468721Z", "iopub.status.busy": "2021-03-07T17:21:58.467870Z", "iopub.status.idle": "2021-03-07T17:21:58.470825Z", "shell.execute_reply": "2021-03-07T17:21:58.470193Z" } }, "outputs": [], "source": [ "# Step 3.a: Set \\partial_t \\beta^i\n", "# First import expressions from BSSN_quantities\n", "BU = Bq.BU\n", "betU = Bq.betU\n", "betaU_dupD = Bq.betaU_dupD\n", "# Define needed quantities\n", "beta_rhsU = ixp.zerorank1()\n", "B_rhsU = ixp.zerorank1()\n", "if par.parval_from_str(thismodule+\"::ShiftEvolutionOption\") == \"GammaDriving2ndOrder_NoCovariant\":\n", " # Step 3.a.i: Compute right-hand side of beta^i\n", " # * \\partial_t \\beta^i = \\beta^j \\beta^i_{,j} + B^i\n", " for i in range(DIM):\n", " beta_rhsU[i] += BU[i]\n", " for j in range(DIM):\n", " beta_rhsU[i] += betaU[j]*betaU_dupD[i][j]\n", " # Compute right-hand side of B^i:\n", " eta = par.Cparameters(\"REAL\", thismodule, [\"eta\"],2.0)\n", "\n", " # Step 3.a.ii: Compute right-hand side of B^i\n", " # * \\partial_t B^i = \\beta^j B^i_{,j} + 3/4 * \\partial_0 \\Lambda^i - eta B^i\n", " # Step 3.a.iii: Define BU_dupD, in terms of derivative of rescaled variable \\bet^i\n", " BU_dupD = ixp.zerorank2()\n", " betU_dupD = ixp.declarerank2(\"betU_dupD\",\"nosym\")\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " BU_dupD[i][j] = betU_dupD[i][j]*rfm.ReU[i] + betU[i]*rfm.ReUdD[i][j]\n", "\n", " # Step 3.a.iv: Compute \\partial_0 \\bar{\\Lambda}^i = (\\partial_t - \\beta^i \\partial_i) \\bar{\\Lambda}^j\n", " Lambdabar_partial0 = ixp.zerorank1()\n", " for i in range(DIM):\n", " Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i]\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " Lambdabar_partial0[j] += -betaU[i]*Brhs.LambdabarU_dupD[j][i]\n", "\n", " # Step 3.a.v: Evaluate RHS of B^i:\n", " for i in range(DIM):\n", " B_rhsU[i] += sp.Rational(3,4)*Lambdabar_partial0[i] - eta*BU[i]\n", " for j in range(DIM):\n", " B_rhsU[i] += betaU[j]*BU_dupD[i][j]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 3.b: [Brown](https://arxiv.org/abs/0902.3652)'s suggested covariant Gamma-driving shift condition \\[Back to [top](#toc)\\]\n", "$$\\label{covgammadriving}$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "### Step 3.b.i: The right-hand side of the $\\partial_t \\beta^i$ equation \\[Back to [top](#toc)\\]\n", "$$\\label{partial_beta}$$\n", "\n", "This is [Brown's](https://arxiv.org/abs/0902.3652) suggested formulation (Eq. 20b; note that Eq. 20a is the same as our lapse condition, as $\\bar{D}_j \\alpha = \\partial_j \\alpha$ for scalar $\\alpha$):\n", "$$\\partial_t \\beta^i = \\left[\\beta^j \\bar{D}_j \\beta^i\\right] + B^{i}$$\n", "Based on the definition of the covariant derivative, we have\n", "$$\n", "\\bar{D}_{j} \\beta^{i} = \\beta^i_{,j} + \\bar{\\Gamma}^i_{mj} \\beta^m,\n", "$$\n", "so the above equation becomes\n", "\\begin{align}\n", "\\partial_t \\beta^i &= \\left[\\beta^j \\left(\\beta^i_{,j} + \\bar{\\Gamma}^i_{mj} \\beta^m\\right)\\right] + B^{i}\\\\\n", "&= {\\underbrace {\\textstyle \\beta^j \\beta^i_{,j}}_{\\text{Term 1}}} + \n", "{\\underbrace {\\textstyle \\beta^j \\bar{\\Gamma}^i_{mj} \\beta^m}_{\\text{Term 2}}} + \n", "{\\underbrace {\\textstyle B^i}_{\\text{Term 3}}} \n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.545297Z", "iopub.status.busy": "2021-03-07T17:21:58.509414Z", "iopub.status.idle": "2021-03-07T17:21:58.603517Z", "shell.execute_reply": "2021-03-07T17:21:58.602939Z" } }, "outputs": [], "source": [ "# Step 3.b: The right-hand side of the \\partial_t \\beta^i equation\n", "if par.parval_from_str(thismodule+\"::ShiftEvolutionOption\") == \"GammaDriving2ndOrder_Covariant\":\n", " # Step 3.b Option 2: \\partial_t \\beta^i = \\left[\\beta^j \\bar{D}_j \\beta^i\\right] + B^{i}\n", " # First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()\n", " Bq.gammabar__inverse_and_derivs()\n", " GammabarUDD = Bq.GammabarUDD\n", " # Then compute right-hand side:\n", " # Term 1: \\beta^j \\beta^i_{,j}\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " beta_rhsU[i] += betaU[j]*betaU_dupD[i][j]\n", "\n", " # Term 2: \\beta^j \\bar{\\Gamma}^i_{mj} \\beta^m\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " for m in range(DIM):\n", " beta_rhsU[i] += betaU[j]*GammabarUDD[i][m][j]*betaU[m]\n", " # Term 3: B^i\n", " for i in range(DIM):\n", " beta_rhsU[i] += BU[i]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "### Step 3.b.ii: The right-hand side of the $\\partial_t B^i$ equation \\[Back to [top](#toc)\\]\n", "$$\\label{partial_upper_b}$$\n", "\n", "$$\\partial_t B^i = \\left[\\beta^j \\bar{D}_j B^i\\right] + \\frac{3}{4}\\left( \\partial_t \\bar{\\Lambda}^{i} - \\beta^j \\bar{D}_j \\bar{\\Lambda}^{i} \\right) - \\eta B^{i}$$\n", "\n", "Based on the definition of the covariant derivative, we have for vector $V^i$\n", "$$\n", "\\bar{D}_{j} V^{i} = V^i_{,j} + \\bar{\\Gamma}^i_{mj} V^m,\n", "$$\n", "so the above equation becomes\n", "\\begin{align}\n", "\\partial_t B^i &= \\left[\\beta^j \\left(B^i_{,j} + \\bar{\\Gamma}^i_{mj} B^m\\right)\\right] + \\frac{3}{4}\\left[ \\partial_t \\bar{\\Lambda}^{i} - \\beta^j \\left(\\bar{\\Lambda}^i_{,j} + \\bar{\\Gamma}^i_{mj} \\bar{\\Lambda}^m\\right) \\right] - \\eta B^{i} \\\\\n", "&= {\\underbrace {\\textstyle \\beta^j B^i_{,j}}_{\\text{Term 1}}} + \n", "{\\underbrace {\\textstyle \\beta^j \\bar{\\Gamma}^i_{mj} B^m}_{\\text{Term 2}}} + \n", "{\\underbrace {\\textstyle \\frac{3}{4}\\partial_t \\bar{\\Lambda}^{i}}_{\\text{Term 3}}} -\n", "{\\underbrace {\\textstyle \\frac{3}{4}\\beta^j \\bar{\\Lambda}^i_{,j}}_{\\text{Term 4}}} -\n", "{\\underbrace {\\textstyle \\frac{3}{4}\\beta^j \\bar{\\Gamma}^i_{mj} \\bar{\\Lambda}^m}_{\\text{Term 5}}} -\n", "{\\underbrace {\\textstyle \\eta B^i}_{\\text{Term 6}}}\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.678281Z", "iopub.status.busy": "2021-03-07T17:21:58.642565Z", "iopub.status.idle": "2021-03-07T17:21:58.700215Z", "shell.execute_reply": "2021-03-07T17:21:58.700720Z" } }, "outputs": [], "source": [ "if par.parval_from_str(thismodule+\"::ShiftEvolutionOption\") == \"GammaDriving2ndOrder_Covariant\":\n", " # Step 3.c: Covariant option:\n", " # \\partial_t B^i = \\beta^j \\bar{D}_j B^i\n", " # + \\frac{3}{4} ( \\partial_t \\bar{\\Lambda}^{i} - \\beta^j \\bar{D}_j \\bar{\\Lambda}^{i} )\n", " # - \\eta B^{i}\n", " # = \\beta^j B^i_{,j} + \\beta^j \\bar{\\Gamma}^i_{mj} B^m\n", " # + \\frac{3}{4}[ \\partial_t \\bar{\\Lambda}^{i}\n", " # - \\beta^j (\\bar{\\Lambda}^i_{,j} + \\bar{\\Gamma}^i_{mj} \\bar{\\Lambda}^m)]\n", " # - \\eta B^{i}\n", " # Term 1, part a: First compute B^i_{,j} using upwinded derivative\n", " BU_dupD = ixp.zerorank2()\n", " betU_dupD = ixp.declarerank2(\"betU_dupD\",\"nosym\")\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " BU_dupD[i][j] = betU_dupD[i][j]*rfm.ReU[i] + betU[i]*rfm.ReUdD[i][j]\n", " # Term 1: \\beta^j B^i_{,j}\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " B_rhsU[i] += betaU[j]*BU_dupD[i][j]\n", " # Term 2: \\beta^j \\bar{\\Gamma}^i_{mj} B^m\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " for m in range(DIM):\n", " B_rhsU[i] += betaU[j]*GammabarUDD[i][m][j]*BU[m]\n", " # Term 3: \\frac{3}{4}\\partial_t \\bar{\\Lambda}^{i}\n", " for i in range(DIM):\n", " B_rhsU[i] += sp.Rational(3,4)*Brhs.Lambdabar_rhsU[i]\n", " # Term 4: -\\frac{3}{4}\\beta^j \\bar{\\Lambda}^i_{,j}\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " B_rhsU[i] += -sp.Rational(3,4)*betaU[j]*Brhs.LambdabarU_dupD[i][j]\n", " # Term 5: -\\frac{3}{4}\\beta^j \\bar{\\Gamma}^i_{mj} \\bar{\\Lambda}^m\n", " for i in range(DIM):\n", " for j in range(DIM):\n", " for m in range(DIM):\n", " B_rhsU[i] += -sp.Rational(3,4)*betaU[j]*GammabarUDD[i][m][j]*Bq.LambdabarU[m]\n", " # Term 6: - \\eta B^i\n", " # eta is a free parameter; we declare it here:\n", " eta = par.Cparameters(\"REAL\", thismodule, [\"eta\"],2.0)\n", " for i in range(DIM):\n", " B_rhsU[i] += -eta*BU[i]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "## Step 3.c: Non-advecting Gamma-driving shift condition (used for evolving \"Static Trumpet\" initial data) \\[Back to [top](#toc)\\]\n", "$$\\label{statictrumpet_nonadvecgammadriving}$$\n", "\n", "\n", "For the shift vector evolution equation, we desire only that the right-hand sides vanish analytically (although numerical error is expected to result in specious evolution). To this end, we adopt the nonadvecting Gamma-driver condition, given by equations 68a and 68b in [Ruchlin, Etienne, & Baumgarte (2018)](https://arxiv.org/pdf/1712.07658.pdf)\n", "\n", "\\begin{align}\n", "\\partial_t \\beta^i &= B^{i} \\\\\n", "\\partial_t B^i &= \\frac{3}{4} \\partial_{t} \\bar{\\Lambda}^{i} - \\eta B^{i},\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.707000Z", "iopub.status.busy": "2021-03-07T17:21:58.706332Z", "iopub.status.idle": "2021-03-07T17:21:58.708570Z", "shell.execute_reply": "2021-03-07T17:21:58.709124Z" } }, "outputs": [], "source": [ "# Step 3.c: Set \\partial_t \\beta^i\n", "\n", "if par.parval_from_str(thismodule+\"::ShiftEvolutionOption\") == \"NonAdvectingGammaDriving\":\n", " # Step 3.c.i: Compute right-hand side of beta^i\n", " # * \\partial_t \\beta^i = B^i\n", " for i in range(DIM):\n", " beta_rhsU[i] += BU[i]\n", "\n", " # Compute right-hand side of B^i:\n", " eta = par.Cparameters(\"REAL\", thismodule, [\"eta\"],2.0)\n", "\n", " # Step 3.c.ii: Compute right-hand side of B^i\n", " # * \\partial_t B^i = 3/4 * \\partial_t \\Lambda^i - eta B^i\n", " # Step 3.c.iii: Evaluate RHS of B^i:\n", " for i in range(DIM):\n", " B_rhsU[i] += sp.Rational(3,4)*Brhs.Lambdabar_rhsU[i] - eta*BU[i]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 4: Rescale right-hand sides of BSSN gauge equations \\[Back to [top](#toc)\\]\n", "$$\\label{rescale}$$\n", "\n", "Next we rescale the right-hand sides of the BSSN equations so that the evolved variables are $\\left\\{h_{i j},a_{i j},\\text{cf}, K, \\lambda^{i}, \\alpha, \\mathcal{V}^i, \\mathcal{B}^i\\right\\}$" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.762715Z", "iopub.status.busy": "2021-03-07T17:21:58.747634Z", "iopub.status.idle": "2021-03-07T17:21:58.765074Z", "shell.execute_reply": "2021-03-07T17:21:58.765648Z" }, "scrolled": true }, "outputs": [], "source": [ "# Step 4: Rescale the BSSN gauge RHS quantities so that the evolved\n", "# variables may remain smooth across coord singularities\n", "vet_rhsU = ixp.zerorank1()\n", "bet_rhsU = ixp.zerorank1()\n", "for i in range(DIM):\n", " vet_rhsU[i] = beta_rhsU[i] / rfm.ReU[i]\n", " bet_rhsU[i] = B_rhsU[i] / rfm.ReU[i]\n", "#print(str(Abar_rhsDD[2][2]).replace(\"**\",\"^\").replace(\"_\",\"\").replace(\"xx\",\"x\").replace(\"sin(x2)\",\"Sin[x2]\").replace(\"sin(2*x2)\",\"Sin[2*x2]\").replace(\"cos(x2)\",\"Cos[x2]\").replace(\"detgbaroverdetghat\",\"detg\"))\n", "#print(str(Dbarbetacontraction).replace(\"**\",\"^\").replace(\"_\",\"\").replace(\"xx\",\"x\").replace(\"sin(x2)\",\"Sin[x2]\").replace(\"detgbaroverdetghat\",\"detg\"))\n", "#print(betaU_dD)\n", "#print(str(trK_rhs).replace(\"xx2\",\"xx3\").replace(\"xx1\",\"xx2\").replace(\"xx0\",\"xx1\").replace(\"**\",\"^\").replace(\"_\",\"\").replace(\"sin(xx2)\",\"Sinx2\").replace(\"xx\",\"x\").replace(\"sin(2*x2)\",\"Sin2x2\").replace(\"cos(x2)\",\"Cosx2\").replace(\"detgbaroverdetghat\",\"detg\"))\n", "#print(str(bet_rhsU[0]).replace(\"xx2\",\"xx3\").replace(\"xx1\",\"xx2\").replace(\"xx0\",\"xx1\").replace(\"**\",\"^\").replace(\"_\",\"\").replace(\"sin(xx2)\",\"Sinx2\").replace(\"xx\",\"x\").replace(\"sin(2*x2)\",\"Sin2x2\").replace(\"cos(x2)\",\"Cosx2\").replace(\"detgbaroverdetghat\",\"detg\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 5: Code Validation against `BSSN.BSSN_gauge_RHSs` NRPy+ module \\[Back to [top](#toc)\\]\n", "$$\\label{code_validation}$$\n", "\n", "Here, as a code validation check, we verify agreement in the SymPy expressions for the RHSs of the BSSN gauge equations between\n", "\n", "1. this tutorial and \n", "2. the NRPy+ [BSSN.BSSN_gauge_RHSs](../edit/BSSN/BSSN_gauge_RHSs.py) module.\n", "\n", "By default, we analyze the RHSs in Spherical coordinates and with the covariant Gamma-driving second-order shift condition, though other coordinate systems & gauge conditions may be chosen." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2021-03-07T17:21:58.772900Z", "iopub.status.busy": "2021-03-07T17:21:58.772178Z", "iopub.status.idle": "2021-03-07T17:21:59.150639Z", "shell.execute_reply": "2021-03-07T17:21:59.151117Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Consistency check between BSSN.BSSN_gauge_RHSs tutorial and NRPy+ module: ALL SHOULD BE ZERO.\n", "alpha_rhs - bssnrhs.alpha_rhs = 0\n", "vet_rhsU[0] - bssnrhs.vet_rhsU[0] = 0\n", "bet_rhsU[0] - bssnrhs.bet_rhsU[0] = 0\n", "vet_rhsU[1] - bssnrhs.vet_rhsU[1] = 0\n", "bet_rhsU[1] - bssnrhs.bet_rhsU[1] = 0\n", "vet_rhsU[2] - bssnrhs.vet_rhsU[2] = 0\n", "bet_rhsU[2] - bssnrhs.bet_rhsU[2] = 0\n" ] } ], "source": [ "# Step 5: We already have SymPy expressions for BSSN gauge RHS expressions\n", "# in terms of other SymPy variables. Even if we reset the\n", "# list of NRPy+ gridfunctions, these *SymPy* expressions for\n", "# BSSN RHS variables *will remain unaffected*.\n", "#\n", "# Here, we will use the above-defined BSSN gauge RHS expressions\n", "# to validate against the same expressions in the\n", "# BSSN/BSSN_gauge_RHSs.py file, to ensure consistency between\n", "# this tutorial and the module itself.\n", "#\n", "# Reset the list of gridfunctions, as registering a gridfunction\n", "# twice will spawn an error.\n", "gri.glb_gridfcs_list = []\n", "\n", "\n", "# Step 5.a: Call the BSSN_gauge_RHSs() function from within the\n", "# BSSN/BSSN_gauge_RHSs.py module,\n", "# which should generate exactly the same expressions as above.\n", "import BSSN.BSSN_gauge_RHSs as Bgrhs\n", "par.set_parval_from_str(\"BSSN.BSSN_gauge_RHSs::ShiftEvolutionOption\",\"GammaDriving2ndOrder_Covariant\")\n", "Bgrhs.BSSN_gauge_RHSs()\n", "\n", "print(\"Consistency check between BSSN.BSSN_gauge_RHSs tutorial and NRPy+ module: ALL SHOULD BE ZERO.\")\n", "\n", "print(\"alpha_rhs - bssnrhs.alpha_rhs = \" + str(alpha_rhs - Bgrhs.alpha_rhs))\n", "\n", "for i in range(DIM):\n", " print(\"vet_rhsU[\"+str(i)+\"] - bssnrhs.vet_rhsU[\"+str(i)+\"] = \" + str(vet_rhsU[i] - Bgrhs.vet_rhsU[i]))\n", " print(\"bet_rhsU[\"+str(i)+\"] - bssnrhs.bet_rhsU[\"+str(i)+\"] = \" + str(bet_rhsU[i] - Bgrhs.bet_rhsU[i]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Step 6: 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-BSSN_time_evolution-BSSN_gauge_RHSs.pdf](Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.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:21:59.155623Z", "iopub.status.busy": "2021-03-07T17:21:59.154923Z", "iopub.status.idle": "2021-03-07T17:22:03.242672Z", "shell.execute_reply": "2021-03-07T17:22:03.243409Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Created Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.tex, and compiled\n", " LaTeX file to PDF file Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.pdf\n" ] } ], "source": [ "import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n", "cmd.output_Jupyter_notebook_to_LaTeXed_PDF(\"Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs\")" ] } ], "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 }