TCT6UJJD7HTLS55CAV4MNVP5QKWKHIO5PZAWO7KDJFUB6DEVLH6AC
Maxwell.old
StaticTrumpet.old
Cactus Code Thorn StaticTrumpet
Author(s) : Erik Schnetter <schnetter@gmail.com>
Maintainer(s): Erik Schnetter <schnetter@gmail.com>
Licence : LGPL
--------------------------------------------------------------------------
1. Purpose
Generate source code for StaticTrumpet initial conditions
Kenneth A. Dennison, Thomas W. Baumgarte: A Simple Family of
Analytical Trumpet Slices of the Schwarzschild Spacetime,
arXiv:1403.5484 [gr-qc], doi:10.1088/0264-9381/31/11/117001.
https://github.com/zachetienne/nrpytutorial/blob/master/Tutorial-ADM_Initial_Data-StaticTrumpet.ipynb
/*
Evaluate the Hamiltonian constraint
*/
void Hamiltonian_constraint(const paramstruct *restrict params, REAL *restrict xx[3],
REAL *restrict in_gfs, REAL *restrict aux_gfs) {
#include "set_Cparameters.h"
#pragma omp parallel for
for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) {
const REAL xx2 = xx[2][i2];
for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) {
const REAL xx1 = xx[1][i1];
for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) {
const REAL xx0 = xx[0][i0];
{
/*
* NRPy+ Finite Difference Code Generation, Step 1 of 2: Read from main memory and compute finite difference stencils:
*/
const double hDD00_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1-2,i2)];
const double hDD00_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1-2,i2)];
const double hDD00_i0_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0,i1-2,i2)];
const double hDD00_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1-2,i2)];
const double hDD00_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1-2,i2)];
const double hDD00_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1-1,i2)];
const double hDD00_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1-1,i2)];
const double hDD00_i0_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0,i1-1,i2)];
const double hDD00_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1-1,i2)];
const double hDD00_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1-1,i2)];
const double hDD00_i0m2_i1_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1,i2)];
const double hDD00_i0m1_i1_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1,i2)];
const double hDD00 = in_gfs[IDX4S(HDD00GF, i0,i1,i2)];
const double hDD00_i0p1_i1_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1,i2)];
const double hDD00_i0p2_i1_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1,i2)];
const double hDD00_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1+1,i2)];
const double hDD00_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1+1,i2)];
const double hDD00_i0_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0,i1+1,i2)];
const double hDD00_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1+1,i2)];
const double hDD00_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1+1,i2)];
const double hDD00_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1+2,i2)];
const double hDD00_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1+2,i2)];
const double hDD00_i0_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0,i1+2,i2)];
const double hDD00_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1+2,i2)];
const double hDD00_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1+2,i2)];
const double hDD01_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1-2,i2)];
const double hDD01_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1-2,i2)];
const double hDD01_i0_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0,i1-2,i2)];
const double hDD01_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1-2,i2)];
const double hDD01_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1-2,i2)];
const double hDD01_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1-1,i2)];
const double hDD01_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1-1,i2)];
const double hDD01_i0_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0,i1-1,i2)];
const double hDD01_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1-1,i2)];
const double hDD01_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1-1,i2)];
const double hDD01_i0m2_i1_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1,i2)];
const double hDD01_i0m1_i1_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1,i2)];
const double hDD01 = in_gfs[IDX4S(HDD01GF, i0,i1,i2)];
const double hDD01_i0p1_i1_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1,i2)];
const double hDD01_i0p2_i1_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1,i2)];
const double hDD01_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1+1,i2)];
const double hDD01_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1+1,i2)];
const double hDD01_i0_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0,i1+1,i2)];
const double hDD01_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1+1,i2)];
const double hDD01_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1+1,i2)];
const double hDD01_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1+2,i2)];
const double hDD01_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1+2,i2)];
const double hDD01_i0_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0,i1+2,i2)];
const double hDD01_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1+2,i2)];
const double hDD01_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1+2,i2)];
const double hDD02_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1-2,i2)];
const double hDD02_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1-2,i2)];
const double hDD02_i0_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0,i1-2,i2)];
const double hDD02_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1-2,i2)];
const double hDD02_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1-2,i2)];
const double hDD02_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1-1,i2)];
const double hDD02_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1-1,i2)];
const double hDD02_i0_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0,i1-1,i2)];
const double hDD02_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1-1,i2)];
const double hDD02_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1-1,i2)];
const double hDD02_i0m2_i1_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1,i2)];
const double hDD02_i0m1_i1_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1,i2)];
const double hDD02 = in_gfs[IDX4S(HDD02GF, i0,i1,i2)];
const double hDD02_i0p1_i1_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1,i2)];
const double hDD02_i0p2_i1_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1,i2)];
const double hDD02_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1+1,i2)];
const double hDD02_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1+1,i2)];
const double hDD02_i0_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0,i1+1,i2)];
const double hDD02_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1+1,i2)];
const double hDD02_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1+1,i2)];
const double hDD02_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1+2,i2)];
const double hDD02_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1+2,i2)];
const double hDD02_i0_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0,i1+2,i2)];
const double hDD02_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1+2,i2)];
const double hDD02_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1+2,i2)];
const double hDD11_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1-2,i2)];
const double hDD11_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1-2,i2)];
const double hDD11_i0_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0,i1-2,i2)];
const double hDD11_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1-2,i2)];
const double hDD11_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1-2,i2)];
const double hDD11_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1-1,i2)];
const double hDD11_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1-1,i2)];
const double hDD11_i0_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0,i1-1,i2)];
const double hDD11_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1-1,i2)];
const double hDD11_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1-1,i2)];
const double hDD11_i0m2_i1_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1,i2)];
const double hDD11_i0m1_i1_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1,i2)];
const double hDD11 = in_gfs[IDX4S(HDD11GF, i0,i1,i2)];
const double hDD11_i0p1_i1_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1,i2)];
const double hDD11_i0p2_i1_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1,i2)];
const double hDD11_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1+1,i2)];
const double hDD11_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1+1,i2)];
const double hDD11_i0_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0,i1+1,i2)];
const double hDD11_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1+1,i2)];
const double hDD11_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1+1,i2)];
const double hDD11_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1+2,i2)];
const double hDD11_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1+2,i2)];
const double hDD11_i0_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0,i1+2,i2)];
const double hDD11_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1+2,i2)];
const double hDD11_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1+2,i2)];
const double hDD12_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1-2,i2)];
const double hDD12_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1-2,i2)];
const double hDD12_i0_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0,i1-2,i2)];
const double hDD12_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1-2,i2)];
const double hDD12_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1-2,i2)];
const double hDD12_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1-1,i2)];
const double hDD12_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1-1,i2)];
const double hDD12_i0_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0,i1-1,i2)];
const double hDD12_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1-1,i2)];
const double hDD12_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1-1,i2)];
const double hDD12_i0m2_i1_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1,i2)];
const double hDD12_i0m1_i1_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1,i2)];
const double hDD12 = in_gfs[IDX4S(HDD12GF, i0,i1,i2)];
const double hDD12_i0p1_i1_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1,i2)];
const double hDD12_i0p2_i1_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1,i2)];
const double hDD12_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1+1,i2)];
const double hDD12_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1+1,i2)];
const double hDD12_i0_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0,i1+1,i2)];
const double hDD12_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1+1,i2)];
const double hDD12_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1+1,i2)];
const double hDD12_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1+2,i2)];
const double hDD12_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1+2,i2)];
const double hDD12_i0_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0,i1+2,i2)];
const double hDD12_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1+2,i2)];
const double hDD12_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1+2,i2)];
const double hDD22_i0m2_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1-2,i2)];
const double hDD22_i0m1_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1-2,i2)];
const double hDD22_i0_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0,i1-2,i2)];
const double hDD22_i0p1_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1-2,i2)];
const double hDD22_i0p2_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1-2,i2)];
const double hDD22_i0m2_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1-1,i2)];
const double hDD22_i0m1_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1-1,i2)];
const double hDD22_i0_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0,i1-1,i2)];
const double hDD22_i0p1_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1-1,i2)];
const double hDD22_i0p2_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1-1,i2)];
const double hDD22_i0m2_i1_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1,i2)];
const double hDD22_i0m1_i1_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1,i2)];
const double hDD22 = in_gfs[IDX4S(HDD22GF, i0,i1,i2)];
const double hDD22_i0p1_i1_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1,i2)];
const double hDD22_i0p2_i1_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1,i2)];
const double hDD22_i0m2_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1+1,i2)];
const double hDD22_i0m1_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1+1,i2)];
const double hDD22_i0_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0,i1+1,i2)];
const double hDD22_i0p1_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1+1,i2)];
const double hDD22_i0p2_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1+1,i2)];
const double hDD22_i0m2_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1+2,i2)];
const double hDD22_i0m1_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1+2,i2)];
const double hDD22_i0_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0,i1+2,i2)];
const double hDD22_i0p1_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1+2,i2)];
const double hDD22_i0p2_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1+2,i2)];
const double aDD00 = in_gfs[IDX4S(ADD00GF, i0,i1,i2)];
const double aDD01 = in_gfs[IDX4S(ADD01GF, i0,i1,i2)];
const double aDD02 = in_gfs[IDX4S(ADD02GF, i0,i1,i2)];
const double aDD11 = in_gfs[IDX4S(ADD11GF, i0,i1,i2)];
const double aDD12 = in_gfs[IDX4S(ADD12GF, i0,i1,i2)];
const double aDD22 = in_gfs[IDX4S(ADD22GF, i0,i1,i2)];
const double lambdaU0_i0_i1m2_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0,i1-2,i2)];
const double lambdaU0_i0_i1m1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0,i1-1,i2)];
const double lambdaU0_i0m2_i1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0-2,i1,i2)];
const double lambdaU0_i0m1_i1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0-1,i1,i2)];
const double lambdaU0 = in_gfs[IDX4S(LAMBDAU0GF, i0,i1,i2)];
const double lambdaU0_i0p1_i1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0+1,i1,i2)];
const double lambdaU0_i0p2_i1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0+2,i1,i2)];
const double lambdaU0_i0_i1p1_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0,i1+1,i2)];
const double lambdaU0_i0_i1p2_i2 = in_gfs[IDX4S(LAMBDAU0GF, i0,i1+2,i2)];
const double lambdaU1_i0_i1m2_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0,i1-2,i2)];
const double lambdaU1_i0_i1m1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0,i1-1,i2)];
const double lambdaU1_i0m2_i1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0-2,i1,i2)];
const double lambdaU1_i0m1_i1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0-1,i1,i2)];
const double lambdaU1 = in_gfs[IDX4S(LAMBDAU1GF, i0,i1,i2)];
const double lambdaU1_i0p1_i1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0+1,i1,i2)];
const double lambdaU1_i0p2_i1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0+2,i1,i2)];
const double lambdaU1_i0_i1p1_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0,i1+1,i2)];
const double lambdaU1_i0_i1p2_i2 = in_gfs[IDX4S(LAMBDAU1GF, i0,i1+2,i2)];
const double lambdaU2_i0_i1m2_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0,i1-2,i2)];
const double lambdaU2_i0_i1m1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0,i1-1,i2)];
const double lambdaU2_i0m2_i1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0-2,i1,i2)];
const double lambdaU2_i0m1_i1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0-1,i1,i2)];
const double lambdaU2 = in_gfs[IDX4S(LAMBDAU2GF, i0,i1,i2)];
const double lambdaU2_i0p1_i1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0+1,i1,i2)];
const double lambdaU2_i0p2_i1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0+2,i1,i2)];
const double lambdaU2_i0_i1p1_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0,i1+1,i2)];
const double lambdaU2_i0_i1p2_i2 = in_gfs[IDX4S(LAMBDAU2GF, i0,i1+2,i2)];
const double trK = in_gfs[IDX4S(TRKGF, i0,i1,i2)];
const double cf_i0m2_i1m2_i2 = in_gfs[IDX4S(CFGF, i0-2,i1-2,i2)];
const double cf_i0m1_i1m2_i2 = in_gfs[IDX4S(CFGF, i0-1,i1-2,i2)];
const double cf_i0_i1m2_i2 = in_gfs[IDX4S(CFGF, i0,i1-2,i2)];
const double cf_i0p1_i1m2_i2 = in_gfs[IDX4S(CFGF, i0+1,i1-2,i2)];
const double cf_i0p2_i1m2_i2 = in_gfs[IDX4S(CFGF, i0+2,i1-2,i2)];
const double cf_i0m2_i1m1_i2 = in_gfs[IDX4S(CFGF, i0-2,i1-1,i2)];
const double cf_i0m1_i1m1_i2 = in_gfs[IDX4S(CFGF, i0-1,i1-1,i2)];
const double cf_i0_i1m1_i2 = in_gfs[IDX4S(CFGF, i0,i1-1,i2)];
const double cf_i0p1_i1m1_i2 = in_gfs[IDX4S(CFGF, i0+1,i1-1,i2)];
const double cf_i0p2_i1m1_i2 = in_gfs[IDX4S(CFGF, i0+2,i1-1,i2)];
const double cf_i0m2_i1_i2 = in_gfs[IDX4S(CFGF, i0-2,i1,i2)];
const double cf_i0m1_i1_i2 = in_gfs[IDX4S(CFGF, i0-1,i1,i2)];
const double cf = in_gfs[IDX4S(CFGF, i0,i1,i2)];
const double cf_i0p1_i1_i2 = in_gfs[IDX4S(CFGF, i0+1,i1,i2)];
const double cf_i0p2_i1_i2 = in_gfs[IDX4S(CFGF, i0+2,i1,i2)];
const double cf_i0m2_i1p1_i2 = in_gfs[IDX4S(CFGF, i0-2,i1+1,i2)];
const double cf_i0m1_i1p1_i2 = in_gfs[IDX4S(CFGF, i0-1,i1+1,i2)];
const double cf_i0_i1p1_i2 = in_gfs[IDX4S(CFGF, i0,i1+1,i2)];
const double cf_i0p1_i1p1_i2 = in_gfs[IDX4S(CFGF, i0+1,i1+1,i2)];
const double cf_i0p2_i1p1_i2 = in_gfs[IDX4S(CFGF, i0+2,i1+1,i2)];
const double cf_i0m2_i1p2_i2 = in_gfs[IDX4S(CFGF, i0-2,i1+2,i2)];
const double cf_i0m1_i1p2_i2 = in_gfs[IDX4S(CFGF, i0-1,i1+2,i2)];
const double cf_i0_i1p2_i2 = in_gfs[IDX4S(CFGF, i0,i1+2,i2)];
const double cf_i0p1_i1p2_i2 = in_gfs[IDX4S(CFGF, i0+1,i1+2,i2)];
const double cf_i0p2_i1p2_i2 = in_gfs[IDX4S(CFGF, i0+2,i1+2,i2)];
const double tmpFD0 = (1.0/12.0)*cf_i0m2_i1_i2;
const double tmpFD1 = -1.0/12.0*cf_i0p2_i1_i2;
const double tmpFD2 = (1.0/12.0)*cf_i0_i1m2_i2;
const double tmpFD3 = -1.0/12.0*cf_i0_i1p2_i2;
const double tmpFD4 = ((invdx0)*(invdx0));
const double tmpFD5 = -5.0/2.0*cf;
const double tmpFD6 = invdx0*invdx1;
const double tmpFD7 = ((invdx1)*(invdx1));
const double tmpFD8 = (1.0/12.0)*hDD00_i0m2_i1_i2;
const double tmpFD9 = -1.0/12.0*hDD00_i0p2_i1_i2;
const double tmpFD10 = (1.0/12.0)*hDD00_i0_i1m2_i2;
const double tmpFD11 = -1.0/12.0*hDD00_i0_i1p2_i2;
const double tmpFD12 = (1.0/12.0)*hDD01_i0m2_i1_i2;
const double tmpFD13 = -1.0/12.0*hDD01_i0p2_i1_i2;
const double tmpFD14 = (1.0/12.0)*hDD01_i0_i1m2_i2;
const double tmpFD15 = -1.0/12.0*hDD01_i0_i1p2_i2;
const double tmpFD16 = (1.0/12.0)*hDD02_i0m2_i1_i2;
const double tmpFD17 = -1.0/12.0*hDD02_i0p2_i1_i2;
const double tmpFD18 = (1.0/12.0)*hDD02_i0_i1m2_i2;
const double tmpFD19 = -1.0/12.0*hDD02_i0_i1p2_i2;
const double tmpFD20 = (1.0/12.0)*hDD11_i0m2_i1_i2;
const double tmpFD21 = -1.0/12.0*hDD11_i0p2_i1_i2;
const double tmpFD22 = (1.0/12.0)*hDD11_i0_i1m2_i2;
const double tmpFD23 = -1.0/12.0*hDD11_i0_i1p2_i2;
const double tmpFD24 = (1.0/12.0)*hDD12_i0m2_i1_i2;
const double tmpFD25 = -1.0/12.0*hDD12_i0p2_i1_i2;
const double tmpFD26 = (1.0/12.0)*hDD12_i0_i1m2_i2;
const double tmpFD27 = -1.0/12.0*hDD12_i0_i1p2_i2;
const double tmpFD28 = (1.0/12.0)*hDD22_i0m2_i1_i2;
const double tmpFD29 = -1.0/12.0*hDD22_i0p2_i1_i2;
const double tmpFD30 = (1.0/12.0)*hDD22_i0_i1m2_i2;
const double tmpFD31 = -1.0/12.0*hDD22_i0_i1p2_i2;
const double tmpFD32 = -5.0/2.0*hDD00;
const double tmpFD33 = -5.0/2.0*hDD01;
const double tmpFD34 = -5.0/2.0*hDD02;
const double tmpFD35 = -5.0/2.0*hDD11;
const double tmpFD36 = -5.0/2.0*hDD12;
const double tmpFD37 = -5.0/2.0*hDD22;
const double cf_dD0 = invdx0*(-2.0/3.0*cf_i0m1_i1_i2 + (2.0/3.0)*cf_i0p1_i1_i2 + tmpFD0 + tmpFD1);
const double cf_dD1 = invdx1*(-2.0/3.0*cf_i0_i1m1_i2 + (2.0/3.0)*cf_i0_i1p1_i2 + tmpFD2 + tmpFD3);
const double cf_dDD00 = tmpFD4*((4.0/3.0)*cf_i0m1_i1_i2 + (4.0/3.0)*cf_i0p1_i1_i2 - tmpFD0 + tmpFD1 + tmpFD5);
const double cf_dDD01 = tmpFD6*((4.0/9.0)*cf_i0m1_i1m1_i2 - 1.0/18.0*cf_i0m1_i1m2_i2 - 4.0/9.0*cf_i0m1_i1p1_i2 + (1.0/18.0)*cf_i0m1_i1p2_i2 - 1.0/18.0*cf_i0m2_i1m1_i2 + (1.0/144.0)*cf_i0m2_i1m2_i2 + (1.0/18.0)*cf_i0m2_i1p1_i2 - 1.0/144.0*cf_i0m2_i1p2_i2 - 4.0/9.0*cf_i0p1_i1m1_i2 + (1.0/18.0)*cf_i0p1_i1m2_i2 + (4.0/9.0)*cf_i0p1_i1p1_i2 - 1.0/18.0*cf_i0p1_i1p2_i2 + (1.0/18.0)*cf_i0p2_i1m1_i2 - 1.0/144.0*cf_i0p2_i1m2_i2 - 1.0/18.0*cf_i0p2_i1p1_i2 + (1.0/144.0)*cf_i0p2_i1p2_i2);
const double cf_dDD11 = tmpFD7*((4.0/3.0)*cf_i0_i1m1_i2 + (4.0/3.0)*cf_i0_i1p1_i2 - tmpFD2 + tmpFD3 + tmpFD5);
const double hDD_dD000 = invdx0*(-2.0/3.0*hDD00_i0m1_i1_i2 + (2.0/3.0)*hDD00_i0p1_i1_i2 + tmpFD8 + tmpFD9);
const double hDD_dD001 = invdx1*(-2.0/3.0*hDD00_i0_i1m1_i2 + (2.0/3.0)*hDD00_i0_i1p1_i2 + tmpFD10 + tmpFD11);
const double hDD_dD010 = invdx0*(-2.0/3.0*hDD01_i0m1_i1_i2 + (2.0/3.0)*hDD01_i0p1_i1_i2 + tmpFD12 + tmpFD13);
const double hDD_dD011 = invdx1*(-2.0/3.0*hDD01_i0_i1m1_i2 + (2.0/3.0)*hDD01_i0_i1p1_i2 + tmpFD14 + tmpFD15);
const double hDD_dD020 = invdx0*(-2.0/3.0*hDD02_i0m1_i1_i2 + (2.0/3.0)*hDD02_i0p1_i1_i2 + tmpFD16 + tmpFD17);
const double hDD_dD021 = invdx1*(-2.0/3.0*hDD02_i0_i1m1_i2 + (2.0/3.0)*hDD02_i0_i1p1_i2 + tmpFD18 + tmpFD19);
const double hDD_dD110 = invdx0*(-2.0/3.0*hDD11_i0m1_i1_i2 + (2.0/3.0)*hDD11_i0p1_i1_i2 + tmpFD20 + tmpFD21);
const double hDD_dD111 = invdx1*(-2.0/3.0*hDD11_i0_i1m1_i2 + (2.0/3.0)*hDD11_i0_i1p1_i2 + tmpFD22 + tmpFD23);
const double hDD_dD120 = invdx0*(-2.0/3.0*hDD12_i0m1_i1_i2 + (2.0/3.0)*hDD12_i0p1_i1_i2 + tmpFD24 + tmpFD25);
const double hDD_dD121 = invdx1*(-2.0/3.0*hDD12_i0_i1m1_i2 + (2.0/3.0)*hDD12_i0_i1p1_i2 + tmpFD26 + tmpFD27);
const double hDD_dD220 = invdx0*(-2.0/3.0*hDD22_i0m1_i1_i2 + (2.0/3.0)*hDD22_i0p1_i1_i2 + tmpFD28 + tmpFD29);
const double hDD_dD221 = invdx1*(-2.0/3.0*hDD22_i0_i1m1_i2 + (2.0/3.0)*hDD22_i0_i1p1_i2 + tmpFD30 + tmpFD31);
const double hDD_dDD0000 = tmpFD4*((4.0/3.0)*hDD00_i0m1_i1_i2 + (4.0/3.0)*hDD00_i0p1_i1_i2 + tmpFD32 - tmpFD8 + tmpFD9);
const double hDD_dDD0001 = tmpFD6*((4.0/9.0)*hDD00_i0m1_i1m1_i2 - 1.0/18.0*hDD00_i0m1_i1m2_i2 - 4.0/9.0*hDD00_i0m1_i1p1_i2 + (1.0/18.0)*hDD00_i0m1_i1p2_i2 - 1.0/18.0*hDD00_i0m2_i1m1_i2 + (1.0/144.0)*hDD00_i0m2_i1m2_i2 + (1.0/18.0)*hDD00_i0m2_i1p1_i2 - 1.0/144.0*hDD00_i0m2_i1p2_i2 - 4.0/9.0*hDD00_i0p1_i1m1_i2 + (1.0/18.0)*hDD00_i0p1_i1m2_i2 + (4.0/9.0)*hDD00_i0p1_i1p1_i2 - 1.0/18.0*hDD00_i0p1_i1p2_i2 + (1.0/18.0)*hDD00_i0p2_i1m1_i2 - 1.0/144.0*hDD00_i0p2_i1m2_i2 - 1.0/18.0*hDD00_i0p2_i1p1_i2 + (1.0/144.0)*hDD00_i0p2_i1p2_i2);
const double hDD_dDD0011 = tmpFD7*((4.0/3.0)*hDD00_i0_i1m1_i2 + (4.0/3.0)*hDD00_i0_i1p1_i2 - tmpFD10 + tmpFD11 + tmpFD32);
const double hDD_dDD0100 = tmpFD4*((4.0/3.0)*hDD01_i0m1_i1_i2 + (4.0/3.0)*hDD01_i0p1_i1_i2 - tmpFD12 + tmpFD13 + tmpFD33);
const double hDD_dDD0101 = tmpFD6*((4.0/9.0)*hDD01_i0m1_i1m1_i2 - 1.0/18.0*hDD01_i0m1_i1m2_i2 - 4.0/9.0*hDD01_i0m1_i1p1_i2 + (1.0/18.0)*hDD01_i0m1_i1p2_i2 - 1.0/18.0*hDD01_i0m2_i1m1_i2 + (1.0/144.0)*hDD01_i0m2_i1m2_i2 + (1.0/18.0)*hDD01_i0m2_i1p1_i2 - 1.0/144.0*hDD01_i0m2_i1p2_i2 - 4.0/9.0*hDD01_i0p1_i1m1_i2 + (1.0/18.0)*hDD01_i0p1_i1m2_i2 + (4.0/9.0)*hDD01_i0p1_i1p1_i2 - 1.0/18.0*hDD01_i0p1_i1p2_i2 + (1.0/18.0)*hDD01_i0p2_i1m1_i2 - 1.0/144.0*hDD01_i0p2_i1m2_i2 - 1.0/18.0*hDD01_i0p2_i1p1_i2 + (1.0/144.0)*hDD01_i0p2_i1p2_i2);
const double hDD_dDD0111 = tmpFD7*((4.0/3.0)*hDD01_i0_i1m1_i2 + (4.0/3.0)*hDD01_i0_i1p1_i2 - tmpFD14 + tmpFD15 + tmpFD33);
const double hDD_dDD0200 = tmpFD4*((4.0/3.0)*hDD02_i0m1_i1_i2 + (4.0/3.0)*hDD02_i0p1_i1_i2 - tmpFD16 + tmpFD17 + tmpFD34);
const double hDD_dDD0201 = tmpFD6*((4.0/9.0)*hDD02_i0m1_i1m1_i2 - 1.0/18.0*hDD02_i0m1_i1m2_i2 - 4.0/9.0*hDD02_i0m1_i1p1_i2 + (1.0/18.0)*hDD02_i0m1_i1p2_i2 - 1.0/18.0*hDD02_i0m2_i1m1_i2 + (1.0/144.0)*hDD02_i0m2_i1m2_i2 + (1.0/18.0)*hDD02_i0m2_i1p1_i2 - 1.0/144.0*hDD02_i0m2_i1p2_i2 - 4.0/9.0*hDD02_i0p1_i1m1_i2 + (1.0/18.0)*hDD02_i0p1_i1m2_i2 + (4.0/9.0)*hDD02_i0p1_i1p1_i2 - 1.0/18.0*hDD02_i0p1_i1p2_i2 + (1.0/18.0)*hDD02_i0p2_i1m1_i2 - 1.0/144.0*hDD02_i0p2_i1m2_i2 - 1.0/18.0*hDD02_i0p2_i1p1_i2 + (1.0/144.0)*hDD02_i0p2_i1p2_i2);
const double hDD_dDD0211 = tmpFD7*((4.0/3.0)*hDD02_i0_i1m1_i2 + (4.0/3.0)*hDD02_i0_i1p1_i2 - tmpFD18 + tmpFD19 + tmpFD34);
const double hDD_dDD1100 = tmpFD4*((4.0/3.0)*hDD11_i0m1_i1_i2 + (4.0/3.0)*hDD11_i0p1_i1_i2 - tmpFD20 + tmpFD21 + tmpFD35);
const double hDD_dDD1101 = tmpFD6*((4.0/9.0)*hDD11_i0m1_i1m1_i2 - 1.0/18.0*hDD11_i0m1_i1m2_i2 - 4.0/9.0*hDD11_i0m1_i1p1_i2 + (1.0/18.0)*hDD11_i0m1_i1p2_i2 - 1.0/18.0*hDD11_i0m2_i1m1_i2 + (1.0/144.0)*hDD11_i0m2_i1m2_i2 + (1.0/18.0)*hDD11_i0m2_i1p1_i2 - 1.0/144.0*hDD11_i0m2_i1p2_i2 - 4.0/9.0*hDD11_i0p1_i1m1_i2 + (1.0/18.0)*hDD11_i0p1_i1m2_i2 + (4.0/9.0)*hDD11_i0p1_i1p1_i2 - 1.0/18.0*hDD11_i0p1_i1p2_i2 + (1.0/18.0)*hDD11_i0p2_i1m1_i2 - 1.0/144.0*hDD11_i0p2_i1m2_i2 - 1.0/18.0*hDD11_i0p2_i1p1_i2 + (1.0/144.0)*hDD11_i0p2_i1p2_i2);
const double hDD_dDD1111 = tmpFD7*((4.0/3.0)*hDD11_i0_i1m1_i2 + (4.0/3.0)*hDD11_i0_i1p1_i2 - tmpFD22 + tmpFD23 + tmpFD35);
const double hDD_dDD1200 = tmpFD4*((4.0/3.0)*hDD12_i0m1_i1_i2 + (4.0/3.0)*hDD12_i0p1_i1_i2 - tmpFD24 + tmpFD25 + tmpFD36);
const double hDD_dDD1201 = tmpFD6*((4.0/9.0)*hDD12_i0m1_i1m1_i2 - 1.0/18.0*hDD12_i0m1_i1m2_i2 - 4.0/9.0*hDD12_i0m1_i1p1_i2 + (1.0/18.0)*hDD12_i0m1_i1p2_i2 - 1.0/18.0*hDD12_i0m2_i1m1_i2 + (1.0/144.0)*hDD12_i0m2_i1m2_i2 + (1.0/18.0)*hDD12_i0m2_i1p1_i2 - 1.0/144.0*hDD12_i0m2_i1p2_i2 - 4.0/9.0*hDD12_i0p1_i1m1_i2 + (1.0/18.0)*hDD12_i0p1_i1m2_i2 + (4.0/9.0)*hDD12_i0p1_i1p1_i2 - 1.0/18.0*hDD12_i0p1_i1p2_i2 + (1.0/18.0)*hDD12_i0p2_i1m1_i2 - 1.0/144.0*hDD12_i0p2_i1m2_i2 - 1.0/18.0*hDD12_i0p2_i1p1_i2 + (1.0/144.0)*hDD12_i0p2_i1p2_i2);
const double hDD_dDD1211 = tmpFD7*((4.0/3.0)*hDD12_i0_i1m1_i2 + (4.0/3.0)*hDD12_i0_i1p1_i2 - tmpFD26 + tmpFD27 + tmpFD36);
const double hDD_dDD2200 = tmpFD4*((4.0/3.0)*hDD22_i0m1_i1_i2 + (4.0/3.0)*hDD22_i0p1_i1_i2 - tmpFD28 + tmpFD29 + tmpFD37);
const double hDD_dDD2201 = tmpFD6*((4.0/9.0)*hDD22_i0m1_i1m1_i2 - 1.0/18.0*hDD22_i0m1_i1m2_i2 - 4.0/9.0*hDD22_i0m1_i1p1_i2 + (1.0/18.0)*hDD22_i0m1_i1p2_i2 - 1.0/18.0*hDD22_i0m2_i1m1_i2 + (1.0/144.0)*hDD22_i0m2_i1m2_i2 + (1.0/18.0)*hDD22_i0m2_i1p1_i2 - 1.0/144.0*hDD22_i0m2_i1p2_i2 - 4.0/9.0*hDD22_i0p1_i1m1_i2 + (1.0/18.0)*hDD22_i0p1_i1m2_i2 + (4.0/9.0)*hDD22_i0p1_i1p1_i2 - 1.0/18.0*hDD22_i0p1_i1p2_i2 + (1.0/18.0)*hDD22_i0p2_i1m1_i2 - 1.0/144.0*hDD22_i0p2_i1m2_i2 - 1.0/18.0*hDD22_i0p2_i1p1_i2 + (1.0/144.0)*hDD22_i0p2_i1p2_i2);
const double hDD_dDD2211 = tmpFD7*((4.0/3.0)*hDD22_i0_i1m1_i2 + (4.0/3.0)*hDD22_i0_i1p1_i2 - tmpFD30 + tmpFD31 + tmpFD37);
const double lambdaU_dD00 = invdx0*(-2.0/3.0*lambdaU0_i0m1_i1_i2 + (1.0/12.0)*lambdaU0_i0m2_i1_i2 + (2.0/3.0)*lambdaU0_i0p1_i1_i2 - 1.0/12.0*lambdaU0_i0p2_i1_i2);
const double lambdaU_dD01 = invdx1*(-2.0/3.0*lambdaU0_i0_i1m1_i2 + (1.0/12.0)*lambdaU0_i0_i1m2_i2 + (2.0/3.0)*lambdaU0_i0_i1p1_i2 - 1.0/12.0*lambdaU0_i0_i1p2_i2);
const double lambdaU_dD10 = invdx0*(-2.0/3.0*lambdaU1_i0m1_i1_i2 + (1.0/12.0)*lambdaU1_i0m2_i1_i2 + (2.0/3.0)*lambdaU1_i0p1_i1_i2 - 1.0/12.0*lambdaU1_i0p2_i1_i2);
const double lambdaU_dD11 = invdx1*(-2.0/3.0*lambdaU1_i0_i1m1_i2 + (1.0/12.0)*lambdaU1_i0_i1m2_i2 + (2.0/3.0)*lambdaU1_i0_i1p1_i2 - 1.0/12.0*lambdaU1_i0_i1p2_i2);
const double lambdaU_dD20 = invdx0*(-2.0/3.0*lambdaU2_i0m1_i1_i2 + (1.0/12.0)*lambdaU2_i0m2_i1_i2 + (2.0/3.0)*lambdaU2_i0p1_i1_i2 - 1.0/12.0*lambdaU2_i0p2_i1_i2);
const double lambdaU_dD21 = invdx1*(-2.0/3.0*lambdaU2_i0_i1m1_i2 + (1.0/12.0)*lambdaU2_i0_i1m2_i2 + (2.0/3.0)*lambdaU2_i0_i1p1_i2 - 1.0/12.0*lambdaU2_i0_i1p2_i2);
/*
* NRPy+ Finite Difference Code Generation, Step 2 of 2: Evaluate SymPy expressions and write to main memory:
*/
const double tmp0 = ((xx0)*(xx0));
const double tmp1 = sin(xx1);
const double tmp2 = hDD02*tmp1;
const double tmp3 = tmp0*tmp2;
const double tmp4 = hDD00 + 1;
const double tmp5 = hDD12*tmp1;
const double tmp6 = tmp0*tmp5;
const double tmp7 = hDD01*tmp3 - tmp4*tmp6;
const double tmp8 = ((tmp7)*(tmp7));
const double tmp9 = 2*hDD01;
const double tmp10 = ((tmp1)*(tmp1));
const double tmp11 = tmp10*((xx0)*(xx0)*(xx0)*(xx0));
const double tmp12 = hDD02*hDD12;
const double tmp13 = ((hDD12)*(hDD12))*tmp11;
const double tmp14 = hDD11*tmp0;
const double tmp15 = tmp0 + tmp14;
const double tmp16 = tmp0*tmp10;
const double tmp17 = ((hDD02)*(hDD02))*tmp16;
const double tmp18 = hDD22*tmp16;
const double tmp19 = tmp16 + tmp18;
const double tmp20 = ((hDD01)*(hDD01))*tmp0;
const double tmp21 = tmp15*tmp4;
const double tmp22 = tmp11*tmp12*tmp9 - tmp13*tmp4 - tmp15*tmp17 - tmp19*tmp20 + tmp19*tmp21;
const double tmp23 = (1.0/((tmp22)*(tmp22)));
const double tmp24 = aDD11*tmp0;
const double tmp25 = tmp23*tmp24;
const double tmp26 = ((xx0)*(xx0)*(xx0));
const double tmp27 = tmp2*xx0;
const double tmp28 = hDD01*tmp26*tmp5 - tmp15*tmp27;
const double tmp29 = ((tmp28)*(tmp28));
const double tmp30 = aDD00*tmp23;
const double tmp31 = -tmp20 + tmp21;
const double tmp32 = aDD22*tmp16;
const double tmp33 = tmp23*tmp32;
const double tmp34 = 2*xx0;
const double tmp35 = aDD01*tmp23;
const double tmp36 = tmp34*tmp35;
const double tmp37 = tmp28*tmp7;
const double tmp38 = tmp0*tmp1;
const double tmp39 = aDD12*tmp38;
const double tmp40 = tmp23*tmp39;
const double tmp41 = tmp31*tmp40;
const double tmp42 = tmp1*tmp34;
const double tmp43 = aDD02*tmp23;
const double tmp44 = tmp42*tmp43;
const double tmp45 = tmp28*tmp44;
const double tmp46 = hDD01*xx0;
const double tmp47 = tmp10*tmp12*tmp26 - tmp19*tmp46;
const double tmp48 = ((tmp47)*(tmp47));
const double tmp49 = -tmp17 + tmp19*tmp4;
const double tmp50 = tmp47*tmp7;
const double tmp51 = tmp49*tmp7;
const double tmp52 = 2*tmp40;
const double tmp53 = tmp36*tmp47;
const double tmp54 = -tmp13 + tmp15*tmp19;
const double tmp55 = tmp28*tmp47;
const double tmp56 = tmp1*xx0;
const double tmp57 = tmp43*tmp56;
const double tmp58 = tmp31*tmp33;
const double tmp59 = tmp35*xx0;
const double tmp60 = tmp31*tmp47;
const double tmp61 = tmp28*tmp49;
const double tmp62 = tmp25*tmp47;
const double tmp63 = tmp54*tmp59;
const double tmp64 = tmp30*tmp54;
const double tmp65 = tmp54*tmp57;
const double tmp66 = ((cf)*(cf));
const double tmp67 = (1.0/(tmp66));
const double tmp68 = (1.0/(tmp22));
const double tmp69 = tmp47*tmp68;
const double tmp70 = ((cf_dD1)*(cf_dD1));
const double tmp71 = tmp49*tmp68;
const double tmp72 = 2*tmp67;
const double tmp73 = ((cf_dD0)*(cf_dD0));
const double tmp74 = tmp54*tmp68;
const double tmp75 = tmp10*xx0;
const double tmp76 = 2*tmp75;
const double tmp77 = hDD22*tmp76;
const double tmp78 = hDD_dD220*tmp16;
const double tmp79 = tmp77 + tmp78;
const double tmp80 = tmp76 + tmp79;
const double tmp81 = tmp68*tmp7;
const double tmp82 = (1.0/2.0)*tmp81;
const double tmp83 = tmp80*tmp82;
const double tmp84 = cos(xx1);
const double tmp85 = hDD02*tmp84;
const double tmp86 = tmp85*xx0;
const double tmp87 = hDD_dD021*tmp1;
const double tmp88 = tmp87*xx0;
const double tmp89 = tmp34*tmp5;
const double tmp90 = hDD_dD120*tmp38;
const double tmp91 = tmp89 + tmp90;
const double tmp92 = -tmp86 - tmp88 + tmp91;
const double tmp93 = (1.0/2.0)*tmp71;
const double tmp94 = tmp92*tmp93;
const double tmp95 = (1.0/(cf));
const double tmp96 = (1.0/2.0)*tmp95;
const double tmp97 = cf_dD1*tmp96;
const double tmp98 = tmp28*tmp68;
const double tmp99 = (1.0/2.0)*tmp98;
const double tmp100 = tmp80*tmp99;
const double tmp101 = (1.0/2.0)*tmp69;
const double tmp102 = tmp101*tmp92;
const double tmp103 = cf_dD0*tmp95;
const double tmp104 = (1.0/2.0)*tmp103;
const double tmp105 = 2*xx1;
const double tmp106 = sin(tmp105);
const double tmp107 = tmp0*tmp106;
const double tmp108 = hDD_dD221*tmp16;
const double tmp109 = tmp38*tmp84;
const double tmp110 = 2*tmp109;
const double tmp111 = hDD22*tmp110;
const double tmp112 = tmp108 + tmp111;
const double tmp113 = tmp107 + tmp112;
const double tmp114 = tmp113*tmp82;
const double tmp115 = tmp86 + tmp88;
const double tmp116 = tmp115 - tmp89 - tmp90;
const double tmp117 = tmp101*tmp116;
const double tmp118 = tmp113*tmp99;
const double tmp119 = (1.0/2.0)*tmp74;
const double tmp120 = tmp116*tmp119;
const double tmp121 = -1.0/2.0*tmp76 - 1.0/2.0*tmp77 - 1.0/2.0*tmp78;
const double tmp122 = tmp121*tmp69;
const double tmp123 = -1.0/2.0*tmp107 - 1.0/2.0*tmp108 - 1.0/2.0*tmp111;
const double tmp124 = tmp123*tmp71;
const double tmp125 = tmp123*tmp69;
const double tmp126 = tmp121*tmp74;
const double tmp127 = tmp31*tmp68;
const double tmp128 = 2*tmp2;
const double tmp129 = hDD_dD020*tmp1;
const double tmp130 = tmp128 + tmp129*tmp34;
const double tmp131 = tmp130*tmp82;
const double tmp132 = hDD_dD000*tmp101;
const double tmp133 = -hDD_dD001 + hDD_dD010*tmp34 + tmp9;
const double tmp134 = tmp133*tmp93;
const double tmp135 = tmp130*tmp99;
const double tmp136 = hDD_dD000*tmp119;
const double tmp137 = tmp101*tmp133;
const double tmp138 = hDD_dD001*tmp101;
const double tmp139 = tmp115 + tmp91;
const double tmp140 = tmp139*tmp82;
const double tmp141 = hDD11*tmp34;
const double tmp142 = hDD_dD110*tmp0;
const double tmp143 = tmp141 + tmp142;
const double tmp144 = tmp143 + tmp34;
const double tmp145 = tmp144*tmp93;
const double tmp146 = hDD_dD001*tmp119;
const double tmp147 = tmp101*tmp144;
const double tmp148 = tmp139*tmp99;
const double tmp149 = 2*tmp0;
const double tmp150 = tmp149*tmp84;
const double tmp151 = hDD_dD121*tmp38;
const double tmp152 = hDD12*tmp150 + 2*tmp151;
const double tmp153 = tmp152*tmp82;
const double tmp154 = hDD_dD111*tmp0;
const double tmp155 = tmp154*tmp93;
const double tmp156 = hDD_dD011*tmp34 - tmp141 - tmp142 - tmp34;
const double tmp157 = tmp101*tmp156;
const double tmp158 = tmp101*tmp154;
const double tmp159 = tmp152*tmp99;
const double tmp160 = tmp119*tmp156;
const double tmp161 = (1.0/2.0)*tmp106;
const double tmp162 = hDD12*xx0;
const double tmp163 = (1.0/(xx0));
const double tmp164 = lambdaU0*tmp163;
const double tmp165 = (1.0/(tmp10));
const double tmp166 = tmp161*tmp165;
const double tmp167 = lambdaU1*tmp163*tmp166 + tmp164;
const double tmp168 = ((tmp1)*(tmp1)*(tmp1));
const double tmp169 = hDD_dD020*tmp168;
const double tmp170 = hDD_dD120*tmp1;
const double tmp171 = hDD02*tmp168;
const double tmp172 = tmp107*tmp5 + tmp149*tmp171;
const double tmp173 = tmp163*tmp172;
const double tmp174 = -tmp173;
const double tmp175 = hDD22*tmp10;
const double tmp176 = tmp163*tmp79;
const double tmp177 = tmp171*tmp34;
const double tmp178 = tmp129*xx0 + tmp2;
const double tmp179 = tmp106*tmp91;
const double tmp180 = cos(tmp105);
const double tmp181 = tmp149*tmp5;
const double tmp182 = tmp0*tmp84;
const double tmp183 = hDD12*tmp182 + tmp151;
const double tmp184 = tmp106*tmp183;
const double tmp185 = tmp106*tmp165;
const double tmp186 = -tmp166*tmp172;
const double tmp187 = tmp5*xx0;
const double tmp188 = (1.0/(tmp1));
const double tmp189 = tmp161*tmp188;
const double tmp190 = -hDD02*tmp189*xx0 - tmp187;
const double tmp191 = tmp115 + tmp190;
const double tmp192 = tmp107*tmp188;
const double tmp193 = (1.0/2.0)*hDD12;
const double tmp194 = tmp183 - tmp192*tmp193 + tmp3;
const double tmp195 = tmp106*tmp194;
const double tmp196 = hDD22*tmp107;
const double tmp197 = tmp112 - tmp196;
const double tmp198 = tmp163*tmp197;
const double tmp199 = 4*hDD22*tmp56*tmp84 + hDD_dD220*tmp110 + hDD_dD221*tmp76 + hDD_dDD2201*tmp16;
const double tmp200 = hDD_dD220*tmp26;
const double tmp201 = hDD00*tmp75 - hDD22*tmp75 + tmp161*tmp46;
const double tmp202 = hDD01*tmp16 + tmp14*tmp161 - 1.0/2.0*tmp196;
const double tmp203 = tmp106*tmp202;
const double tmp204 = (1.0/2.0)*tmp127;
const double tmp205 = -tmp198;
const double tmp206 = tmp112*tmp163;
const double tmp207 = hDD_dD220*tmp107;
const double tmp208 = tmp165*tmp180;
const double tmp209 = (1.0/(tmp168));
const double tmp210 = tmp106*tmp209*tmp84;
const double tmp211 = -tmp208 + tmp210;
const double tmp212 = tmp18*tmp211;
const double tmp213 = tmp208 - tmp210;
const double tmp214 = -tmp18*tmp213;
const double tmp215 = tmp121*tmp98 + tmp123*tmp81;
const double tmp216 = tmp100 + tmp102;
const double tmp217 = tmp83 + tmp94;
const double tmp218 = -tmp163;
const double tmp219 = tmp204*tmp80 + tmp218 + tmp82*tmp92;
const double tmp220 = tmp19*tmp219;
const double tmp221 = tmp216*tmp27 + tmp217*tmp6 + tmp220;
const double tmp222 = tmp215*tmp221;
const double tmp223 = 3*tmp98;
const double tmp224 = tmp219*tmp221;
const double tmp225 = 3*tmp74;
const double tmp226 = tmp114 + tmp117;
const double tmp227 = tmp118 + tmp120;
const double tmp228 = tmp113*tmp204 + tmp116*tmp99 - tmp166;
const double tmp229 = tmp19*tmp228;
const double tmp230 = tmp226*tmp6 + tmp227*tmp27 + tmp229;
const double tmp231 = tmp215*tmp230;
const double tmp232 = 3*tmp81;
const double tmp233 = tmp221*tmp228;
const double tmp234 = 3*tmp69;
const double tmp235 = tmp219*tmp230;
const double tmp236 = tmp228*tmp230;
const double tmp237 = 3*tmp71;
const double tmp238 = tmp19*tmp215;
const double tmp239 = tmp122 + tmp124 + tmp161;
const double tmp240 = tmp125 + tmp126 + tmp75;
const double tmp241 = tmp238 + tmp239*tmp6 + tmp240*tmp27;
const double tmp242 = 3*tmp127;
const double tmp243 = tmp219*tmp241;
const double tmp244 = tmp228*tmp241;
const double tmp245 = tmp216*tmp221;
const double tmp246 = tmp215*tmp27 + tmp239*tmp46 + tmp240*tmp4;
const double tmp247 = tmp216*tmp246;
const double tmp248 = tmp221*tmp227;
const double tmp249 = tmp227*tmp246;
const double tmp250 = tmp226*tmp230;
const double tmp251 = tmp15*tmp239 + tmp215*tmp6 + tmp240*tmp46;
const double tmp252 = tmp226*tmp251;
const double tmp253 = tmp217*tmp230;
const double tmp254 = tmp217*tmp251;
const double tmp255 = 2*tmp240;
const double tmp256 = 2*tmp239;
const double tmp257 = tmp216*tmp4;
const double tmp258 = tmp217*tmp46 + tmp219*tmp27 + tmp257;
const double tmp259 = tmp216*tmp258;
const double tmp260 = hDD_dD000*tmp99 + tmp130*tmp204 + tmp133*tmp82;
const double tmp261 = tmp131 + tmp132 + tmp134;
const double tmp262 = tmp135 + tmp136 + tmp137;
const double tmp263 = tmp19*tmp260 + tmp261*tmp6 + tmp262*tmp27;
const double tmp264 = tmp216*tmp263;
const double tmp265 = tmp227*tmp258;
const double tmp266 = tmp227*tmp263;
const double tmp267 = tmp240*tmp258;
const double tmp268 = tmp15*tmp217 + tmp216*tmp46 + tmp219*tmp6;
const double tmp269 = tmp226*tmp268;
const double tmp270 = hDD_dD001*tmp99 + tmp139*tmp204 + tmp144*tmp82;
const double tmp271 = tmp138 + tmp140 + tmp145 + tmp218;
const double tmp272 = tmp146 + tmp147 + tmp148;
const double tmp273 = tmp19*tmp270 + tmp27*tmp272 + tmp271*tmp6;
const double tmp274 = tmp226*tmp273;
const double tmp275 = tmp217*tmp268;
const double tmp276 = tmp217*tmp273;
const double tmp277 = tmp226*tmp46 + tmp227*tmp4 + tmp228*tmp27;
const double tmp278 = tmp216*tmp277;
const double tmp279 = tmp216*tmp273;
const double tmp280 = tmp15*tmp226;
const double tmp281 = tmp227*tmp46 + tmp228*tmp6 + tmp280;
const double tmp282 = tmp217*tmp281;
const double tmp283 = tmp152*tmp204 + tmp154*tmp82 + tmp156*tmp99;
const double tmp284 = tmp153 + tmp155 + tmp157;
const double tmp285 = tmp158 + tmp159 + tmp160 + xx0;
const double tmp286 = tmp19*tmp283 + tmp27*tmp285 + tmp284*tmp6;
const double tmp287 = tmp217*tmp286;
const double tmp288 = tmp226*tmp281;
const double tmp289 = tmp226*tmp286;
const double tmp290 = tmp227*tmp277;
const double tmp291 = tmp227*tmp273;
const double tmp292 = tmp239*tmp268;
const double tmp293 = tmp239*tmp281;
const double tmp294 = tmp240*tmp277;
const double tmp295 = tmp2*tmp34;
const double tmp296 = tmp119*tmp260 + tmp204*tmp215 + tmp219*tmp98 + tmp228*tmp81 + tmp270*tmp69 + tmp283*tmp93;
const double tmp297 = tmp119*tmp261 + tmp204*tmp239 + tmp217*tmp98 + tmp226*tmp81 + tmp271*tmp69 + tmp284*tmp93;
const double tmp298 = tmp119*tmp262 + tmp204*tmp240 + tmp216*tmp98 + tmp227*tmp81 + tmp272*tmp69 + tmp285*tmp93;
const double tmp299 = 2*hDD_dD010;
const double tmp300 = hDD_dD001 - tmp9;
const double tmp301 = hDD_dDD0001 - tmp163*tmp300;
const double tmp302 = tmp163*tmp2;
const double tmp303 = tmp163*tmp178;
const double tmp304 = 2*hDD_dD011;
const double tmp305 = hDD_dD000*xx0;
const double tmp306 = hDD_dD011*xx0;
const double tmp307 = hDD00*xx0 - hDD11*xx0 + tmp306;
const double tmp308 = tmp163*tmp307;
const double tmp309 = tmp305 - 2*tmp308;
const double tmp310 = hDD_dD010*xx0;
const double tmp311 = hDD01 + tmp310;
const double tmp312 = tmp163*tmp311;
const double tmp313 = tmp106*tmp188;
const double tmp314 = hDD02*tmp313;
const double tmp315 = tmp163*tmp191;
const double tmp316 = -2*tmp315;
const double tmp317 = tmp163*tmp201;
const double tmp318 = hDD_dD000*tmp75;
const double tmp319 = -2*tmp317 + tmp318;
const double tmp320 = tmp115*tmp163;
const double tmp321 = tmp163*tmp190;
const double tmp322 = -2*tmp321;
const double tmp323 = tmp258*tmp262;
const double tmp324 = tmp258*tmp272;
const double tmp325 = tmp262*tmp4;
const double tmp326 = tmp260*tmp27 + tmp261*tmp46 + tmp325;
const double tmp327 = tmp216*tmp326;
const double tmp328 = tmp272*tmp4;
const double tmp329 = tmp27*tmp270 + tmp271*tmp46 + tmp328;
const double tmp330 = tmp216*tmp329;
const double tmp331 = tmp272*tmp326;
const double tmp332 = tmp262*tmp329;
const double tmp333 = tmp272*tmp329;
const double tmp334 = tmp217*tmp277;
const double tmp335 = tmp219*tmp246;
const double tmp336 = tmp261*tmp268;
const double tmp337 = 2*tmp261;
const double tmp338 = tmp221*tmp260;
const double tmp339 = 2*tmp260;
const double tmp340 = tmp221*tmp270;
const double tmp341 = tmp246*tmp270;
const double tmp342 = tmp268*tmp271;
const double tmp343 = tmp271*tmp277;
const double tmp344 = tmp219*tmp258;
const double tmp345 = tmp219*tmp263;
const double tmp346 = tmp219*tmp277;
const double tmp347 = tmp219*tmp273;
const double tmp348 = tmp258*tmp270;
const double tmp349 = tmp263*tmp270;
const double tmp350 = tmp260*tmp273;
const double tmp351 = tmp270*tmp277;
const double tmp352 = tmp270*tmp273;
const double tmp353 = tmp15*tmp261 + tmp260*tmp6 + tmp262*tmp46;
const double tmp354 = tmp217*tmp353;
const double tmp355 = tmp217*tmp329;
const double tmp356 = tmp27*tmp283 + tmp284*tmp46 + tmp285*tmp4;
const double tmp357 = tmp217*tmp356;
const double tmp358 = tmp15*tmp271;
const double tmp359 = tmp270*tmp6 + tmp272*tmp46 + tmp358;
const double tmp360 = tmp217*tmp359;
const double tmp361 = tmp271*tmp353;
const double tmp362 = tmp271*tmp329;
const double tmp363 = tmp261*tmp359;
const double tmp364 = tmp271*tmp356;
const double tmp365 = tmp271*tmp359;
const double tmp366 = hDD01*tmp34;
const double tmp367 = -lambdaU1 + lambdaU_dD01;
const double tmp368 = lambdaU_dD11*tmp163 + tmp164;
const double tmp369 = tmp163*tmp188;
const double tmp370 = lambdaU2*tmp163;
const double tmp371 = lambdaU_dD21*tmp369 + tmp161*tmp209*tmp370 - tmp165*tmp370*tmp84;
const double tmp372 = tmp162*tmp313;
const double tmp373 = hDD_dD120*tmp192;
const double tmp374 = hDD_dDD1101*tmp0;
const double tmp375 = tmp0*tmp9 + tmp154;
const double tmp376 = tmp163*tmp375;
const double tmp377 = -tmp376;
const double tmp378 = hDD_dD110*tmp26;
const double tmp379 = tmp143*tmp163;
const double tmp380 = tmp0*((tmp106)*(tmp106))*tmp193;
const double tmp381 = tmp209*tmp380;
const double tmp382 = tmp161*tmp375;
const double tmp383 = -tmp165*tmp203;
const double tmp384 = tmp211*tmp6;
const double tmp385 = -tmp213*tmp6;
const double tmp386 = tmp281*tmp284;
const double tmp387 = tmp271*tmp281;
const double tmp388 = tmp15*tmp284;
const double tmp389 = tmp283*tmp6 + tmp285*tmp46 + tmp388;
const double tmp390 = tmp226*tmp389;
const double tmp391 = tmp226*tmp359;
const double tmp392 = tmp284*tmp359;
const double tmp393 = tmp271*tmp389;
const double tmp394 = tmp227*tmp268;
const double tmp395 = tmp228*tmp251;
const double tmp396 = tmp268*tmp272;
const double tmp397 = tmp272*tmp277;
const double tmp398 = 2*tmp285;
const double tmp399 = tmp277*tmp285;
const double tmp400 = tmp230*tmp283;
const double tmp401 = 2*tmp283;
const double tmp402 = tmp230*tmp270;
const double tmp403 = tmp251*tmp270;
const double tmp404 = tmp228*tmp268;
const double tmp405 = tmp228*tmp273;
const double tmp406 = tmp228*tmp281;
const double tmp407 = tmp228*tmp286;
const double tmp408 = tmp273*tmp283;
const double tmp409 = tmp268*tmp270;
const double tmp410 = tmp270*tmp281;
const double tmp411 = tmp270*tmp286;
const double tmp412 = tmp227*tmp353;
const double tmp413 = tmp227*tmp329;
const double tmp414 = tmp227*tmp356;
const double tmp415 = tmp227*tmp359;
const double tmp416 = tmp285*tmp329;
const double tmp417 = tmp272*tmp353;
const double tmp418 = tmp272*tmp356;
const double tmp419 = tmp272*tmp359;
const double tmp420 = tmp215*tmp246;
const double tmp421 = tmp239*tmp277 + tmp253;
const double tmp422 = tmp228*tmp277;
const double tmp423 = 2*tmp402;
const double tmp424 = tmp272*tmp273;
const double tmp425 = tmp397 + tmp424;
const double tmp426 = tmp226*tmp356;
const double tmp427 = tmp271*tmp286;
const double tmp428 = tmp387 + tmp427;
const double tmp429 = tmp261*tmp273 + tmp355;
const double tmp430 = tmp262*tmp263;
const double tmp431 = tmp215*tmp277;
const double tmp432 = tmp215*tmp258;
const double tmp433 = tmp230*tmp260;
const double tmp434 = tmp241*tmp260;
const double tmp435 = tmp228*tmp258;
const double tmp436 = 2*tmp340;
const double tmp437 = tmp228*tmp246;
const double tmp438 = tmp241*tmp270;
const double tmp439 = 2*tmp438;
const double tmp440 = tmp261*tmp281;
const double tmp441 = tmp261*tmp286 + tmp357;
const double tmp442 = tmp227*tmp326;
const double tmp443 = tmp263*tmp272;
const double tmp444 = tmp324 + tmp443;
const double tmp445 = tmp226*tmp329;
const double tmp446 = tmp271*tmp273;
const double tmp447 = tmp342 + tmp446;
const double tmp448 = tmp262*tmp277;
const double tmp449 = tmp262*tmp273;
const double tmp450 = tmp239*tmp356 + tmp287;
const double tmp451 = tmp221*tmp272;
const double tmp452 = tmp246*tmp272 + tmp451;
const double tmp453 = tmp226*tmp277;
const double tmp454 = tmp230*tmp271;
const double tmp455 = tmp251*tmp271 + tmp454;
const double tmp456 = tmp240*tmp329;
const double tmp457 = tmp230*tmp261 + tmp334;
const double tmp458 = tmp239*tmp329 + tmp276;
const double tmp459 = tmp240*tmp326;
const double tmp460 = tmp221*tmp262;
const double tmp461 = tmp163*tmp183;
const double tmp462 = -tmp115*tmp166 - tmp461;
const double tmp463 = -tmp213*tmp27 + tmp462;
const double tmp464 = tmp0*tmp129;
const double tmp465 = tmp34*tmp84;
const double tmp466 = tmp163*tmp194;
const double tmp467 = -tmp466;
const double tmp468 = -tmp166*tmp191 + tmp467;
const double tmp469 = hDD_dD021*tmp465 + hDD_dDD0211*tmp56 - tmp27 + tmp464 + tmp468;
const double tmp470 = (1.0/2.0)*tmp4;
const double tmp471 = lambdaU2*tmp1;
const double tmp472 = tmp163*tmp91;
const double tmp473 = -tmp166*tmp178 - tmp472 + tmp5;
const double tmp474 = hDD_dD020*xx0;
const double tmp475 = hDD_dDD0201*tmp56 + tmp474*tmp84 + tmp85 + tmp87;
const double tmp476 = -tmp170*xx0 - tmp189*tmp474;
const double tmp477 = tmp161*tmp307 - tmp166*tmp201;
const double tmp478 = tmp163*tmp202;
const double tmp479 = -tmp478;
const double tmp480 = (1.0/2.0)*tmp27;
const double tmp481 = (1.0/2.0)*lambdaU_dD10;
const double tmp482 = (1.0/2.0)*lambdaU_dD20;
const double tmp483 = (1.0/4.0)*lambdaU2;
const double tmp484 = -hDD01*tmp313*tmp483 + lambdaU_dD00*tmp480 - tmp101*(tmp316 + tmp473 + tmp475) - tmp101*(-tmp315 - tmp320 + tmp475 + tmp476) - tmp119*(hDD_dDD0200*tmp56 + tmp129 + tmp302 - tmp303) + tmp167*tmp480 + tmp187*tmp481 + tmp19*tmp369*tmp482 - tmp204*(tmp0*tmp169 + tmp161*tmp190 + tmp161*tmp191 + tmp174 - tmp177) + tmp296*(tmp221 + tmp246) + tmp297*(tmp273 + tmp277) + tmp298*(tmp258 + tmp263) - tmp470*tmp471 - tmp82*(tmp205 + tmp300*tmp75 + tmp477) - tmp82*(hDD00*tmp1*tmp465 + hDD_dD001*tmp75 + tmp161*tmp306 + tmp180*tmp46 - tmp185*tmp201 - tmp206 + tmp479) - tmp99*(-hDD_dD220*tmp75 + tmp161*tmp310 - tmp317 + tmp318) - tmp99*(hDD00*tmp10 + tmp161*tmp311 + tmp175 - tmp176 + tmp319);
const double tmp485 = tmp215*tmp251;
const double tmp486 = tmp240*tmp268 + tmp248;
const double tmp487 = tmp273*tmp285 + tmp415;
const double tmp488 = tmp284*tmp286;
const double tmp489 = tmp219*tmp268;
const double tmp490 = tmp216*tmp353;
const double tmp491 = tmp215*tmp268;
const double tmp492 = tmp215*tmp281;
const double tmp493 = tmp219*tmp281;
const double tmp494 = tmp219*tmp251;
const double tmp495 = tmp221*tmp283;
const double tmp496 = tmp241*tmp283;
const double tmp497 = tmp217*tmp389;
const double tmp498 = tmp258*tmp285;
const double tmp499 = tmp263*tmp285 + tmp412;
const double tmp500 = tmp268*tmp284;
const double tmp501 = tmp273*tmp284;
const double tmp502 = tmp216*tmp359;
const double tmp503 = tmp240*tmp359 + tmp291;
const double tmp504 = tmp221*tmp285 + tmp394;
const double tmp505 = tmp239*tmp389;
const double tmp506 = tmp230*tmp284;
const double tmp507 = tmp240*tmp353 + tmp266;
const double tmp508 = tmp239*tmp359;
const double tmp509 = tmp216*tmp268;
const double tmp510 = hDD_dD121*tmp150 + hDD_dDD1211*tmp38 + tmp115*xx0 - tmp166*tmp183 - tmp166*tmp194 + tmp170*tmp26 + tmp191*xx0 - tmp6;
const double tmp511 = (1.0/2.0)*tmp107;
const double tmp512 = hDD_dD011*tmp16 + hDD_dD111*tmp511 + tmp109*tmp9 - tmp112*tmp166 + tmp14*tmp180 + tmp201*xx0 + tmp383;
const double tmp513 = (1.0/2.0)*tmp371;
const double tmp514 = hDD12*tmp465 + hDD_dD120*tmp182 + hDD_dD121*tmp42 + hDD_dDD1201*tmp38;
const double tmp515 = hDD_dD010*tmp16 + tmp479;
const double tmp516 = (1.0/2.0)*tmp6;
const double tmp517 = (1.0/2.0)*tmp46;
const double tmp518 = -tmp101*(-1.0/2.0*tmp373 - 2*tmp461 + tmp464 + tmp467 + tmp514) - tmp101*(-tmp166*tmp91 + tmp178*xx0 + tmp27 - 3*tmp466 + tmp514) - tmp106*tmp15*tmp369*tmp483 - tmp119*(hDD_dDD1200*tmp38 + tmp170*tmp34 - 2*tmp472 + 4*tmp5) + tmp167*tmp516 + tmp19*tmp513 - tmp204*(hDD_dD120*tmp168*tmp26 + tmp161*tmp194 + tmp186 - tmp188*tmp380 + tmp190*tmp75) + tmp296*(tmp230 + tmp251) + tmp297*(tmp281 + tmp286) + tmp298*(tmp268 + tmp273) + tmp367*tmp480 + tmp368*tmp516 - tmp471*tmp517 - tmp82*(-tmp166*tmp197 - tmp166*tmp202 + tmp307*tmp75 + tmp382) - tmp99*(hDD_dD110*tmp511 - 1.0/2.0*tmp207 + tmp515) - tmp99*(hDD01*tmp75 + tmp143*tmp161 - tmp166*tmp79 + tmp311*tmp75 - 3*tmp478);
const double tmp519 = 2*tmp413;
const double tmp520 = tmp391 + tmp426;
const double tmp521 = 2*tmp265;
const double tmp522 = 2*tmp442;
const double tmp523 = tmp226*tmp353 + tmp445;
const double tmp524 = tmp228*tmp263;
const double tmp525 = tmp269 + tmp453;
const double tmp526 = tmp215*tmp273;
const double tmp527 = tmp211*tmp27 + tmp462;
const double tmp528 = 2*tmp360;
const double tmp529 = tmp330 + tmp490;
const double tmp530 = 2*tmp282;
const double tmp531 = 2*tmp497;
const double tmp532 = tmp216*tmp356 + tmp502;
const double tmp533 = tmp219*tmp286;
const double tmp534 = tmp278 + tmp509;
const double tmp535 = tmp437 + tmp494;
const double tmp536 = tmp277*tmp283 + tmp410;
const double tmp537 = tmp284*tmp356;
const double tmp538 = tmp262*tmp353;
const double tmp539 = tmp260*tmp268 + tmp348;
const double tmp540 = tmp262*tmp268;
const double tmp541 = tmp285*tmp326;
const double tmp542 = tmp262*tmp359;
const double tmp543 = tmp260*tmp281 + tmp351;
const double tmp544 = tmp258*tmp283 + tmp409;
const double tmp545 = tmp284*tmp329;
const double tmp546 = tmp261*tmp389;
const double tmp547 = tmp422 + tmp493;
const double tmp548 = tmp277*tmp284;
const double tmp549 = tmp246*tmp283 + tmp403;
const double tmp550 = tmp435 + tmp489;
const double tmp551 = tmp251*tmp260 + tmp341;
const double tmp552 = -tmp166*tmp190;
const double tmp553 = -tmp295 + tmp372 + tmp552;
const double tmp554 = hDD_dDD0101*xx0;
const double tmp555 = lambdaU_dD00*tmp517 - tmp101*(-hDD_dD110*xx0 + tmp305 - tmp308 + tmp554) - tmp101*(hDD00 + hDD11 + hDD_dD011 + tmp309 - tmp379 + tmp554) - tmp119*(hDD01*tmp163 + hDD_dD010 + hDD_dDD0100*xx0 - tmp312) + tmp15*tmp163*tmp481 + tmp162*tmp482 - tmp204*(tmp477 + tmp515) + tmp27*tmp513 + tmp296*(tmp268 + tmp277) + tmp297*(tmp356 + tmp359) + tmp298*(tmp329 + tmp353) + tmp367*tmp470 + tmp368*tmp517 - tmp82*(tmp468 + tmp552) - tmp93*(hDD_dD001*xx0 + hDD_dD010*tmp0 - hDD_dD111*xx0 + hDD_dDD0111*xx0 + tmp300*xx0 + tmp377) - tmp99*(-tmp321 + tmp476) - tmp99*(tmp322 + tmp473);
aux_gfs[IDX4S(HGF, i0, i1, i2)] = -aDD00*(tmp25*tmp48 + tmp29*tmp33 + tmp30*((tmp54)*(tmp54)) + tmp45*tmp54 + tmp52*tmp55 + tmp53*tmp54) - aDD01*tmp34*(tmp33*tmp37 + tmp40*tmp50 + tmp40*tmp61 + tmp47*tmp64 + tmp48*tmp59 + tmp49*tmp62 + tmp49*tmp63 + tmp55*tmp57 + tmp65*tmp7) - aDD02*tmp42*(tmp28*tmp58 + tmp28*tmp64 + tmp29*tmp57 + tmp31*tmp65 + tmp37*tmp40 + tmp40*tmp60 + tmp55*tmp59 + tmp62*tmp7 + tmp63*tmp7) - tmp24*(tmp25*((tmp49)*(tmp49)) + tmp30*tmp48 + tmp33*tmp8 + tmp44*tmp50 + tmp49*tmp53 + tmp51*tmp52) - tmp32*(tmp25*tmp8 + tmp29*tmp30 + ((tmp31)*(tmp31))*tmp33 + tmp31*tmp45 + tmp36*tmp37 + 2*tmp41*tmp7) - 2*tmp39*(tmp25*tmp51 + tmp30*tmp55 + tmp37*tmp57 + tmp40*tmp8 + tmp41*tmp49 + tmp50*tmp59 + tmp57*tmp60 + tmp58*tmp7 + tmp59*tmp61) + tmp66*(-4*cf_dD0*cf_dD1*tmp67*tmp69 - 8*tmp127*(-tmp104*(-tmp125 - tmp126) - tmp97*(-tmp122 - tmp124)) + tmp127*(-hDD02*lambdaU2*tmp75 - lambdaU2*tmp161*tmp162 - tmp101*(-tmp185*tmp79 - 3*tmp198 + tmp199) - tmp101*(tmp199 + tmp205 - 2*tmp206 - tmp207) - tmp119*(hDD_dD220*tmp76 + hDD_dDD2200*tmp16 + 4*tmp175 - 2*tmp176) + tmp127*(tmp221*tmp255 + tmp240*tmp246) + tmp127*(tmp230*tmp256 + tmp239*tmp251) + tmp167*tmp19 - tmp204*(((tmp1)*(tmp1)*(tmp1)*(tmp1))*tmp200 + tmp161*tmp197 + tmp201*tmp76 + tmp203) + tmp215*tmp241*tmp242 + tmp222*tmp223 + tmp223*tmp243 + tmp224*tmp225 + tmp231*tmp232 + tmp232*tmp244 + tmp233*tmp234 + tmp234*tmp235 + tmp236*tmp237 + tmp296*(2*tmp238 + tmp240*tmp295 + tmp256*tmp6) + tmp297*(tmp181*tmp226 + tmp227*tmp295 + 2*tmp229) + tmp298*(tmp181*tmp217 + tmp216*tmp295 + 2*tmp220) + tmp69*(tmp265 + 2*tmp266) + tmp69*(tmp269 + 2*tmp274) + tmp69*(tmp278 + 2*tmp279) + tmp69*(tmp282 + 2*tmp287) + tmp71*(tmp288 + 2*tmp289) + tmp71*(tmp290 + 2*tmp291) + tmp74*(tmp259 + 2*tmp264) + tmp74*(tmp275 + 2*tmp276) + tmp81*(2*tmp248 + tmp249) + tmp81*(2*tmp250 + tmp252) + tmp81*(tmp255*tmp273 + tmp294) + tmp81*(tmp256*tmp286 + tmp293) - tmp82*(tmp186 + tmp191*tmp76 + tmp195) - tmp82*(tmp115*tmp76 + 4*tmp16*tmp85 - 3.0/2.0*tmp172*tmp185 + tmp180*tmp181 + tmp184) - tmp93*(hDD22*(tmp149*((tmp84)*(tmp84)) - 2*tmp16) + 4*hDD_dD221*tmp109 + hDD_dDD2211*tmp16 + tmp10*tmp200 - tmp112*tmp185 - tmp185*tmp197 + tmp212 + tmp214) + tmp98*(2*tmp245 + tmp247) + tmp98*(2*tmp253 + tmp254) + tmp98*(tmp255*tmp263 + tmp267) + tmp98*(tmp256*tmp273 + tmp292) - tmp99*(tmp107*tmp170 + tmp149*tmp169 + tmp174) - tmp99*(-3*tmp173 + tmp177 + tmp178*tmp76 + tmp179)) - 16*tmp69*(-tmp104*(-tmp146 - tmp147 - tmp148) + tmp96*(cf_dD1*tmp103 - cf_dDD01) - tmp97*(-tmp138 - tmp140 - tmp145)) + tmp69*(tmp127*(tmp233 + tmp535) + tmp127*(tmp282 + tmp525) + tmp127*(tmp509 + tmp521) + tmp555 + tmp69*(2*tmp333 + tmp542) + tmp69*(tmp352 + tmp543) + tmp69*(tmp417 + 2*tmp541) + tmp69*(tmp263*tmp283 + tmp544) + tmp69*(tmp364 + tmp365 + tmp546) + tmp69*(tmp284*tmp353 + tmp365 + tmp545) + tmp71*(tmp408 + tmp536) + tmp71*(2*tmp416 + tmp419) + tmp71*(tmp392 + tmp393 + tmp537) + tmp74*(2*tmp331 + tmp538) + tmp74*(tmp349 + tmp539) + tmp74*(tmp361 + tmp362 + tmp363) + tmp81*(tmp396 + 2*tmp498) + tmp81*(tmp405 + tmp547) + tmp81*(tmp495 + tmp549) + tmp81*(tmp497 + tmp520) + tmp81*(tmp502 + tmp519) + tmp81*(tmp387 + tmp500 + tmp548) - tmp82*(tmp527 + tmp553) + tmp98*(2*tmp324 + tmp540) + tmp98*(tmp340 + tmp551) + tmp98*(tmp360 + tmp523) + tmp98*(tmp490 + tmp522) + tmp98*(tmp524 + tmp550) + tmp98*(tmp342 + tmp343 + tmp440)) + tmp69*(tmp127*(tmp235 + tmp535) + tmp127*(tmp265 + tmp534) + tmp127*(tmp453 + tmp530) + tmp555 + tmp69*(tmp352 + tmp544) + tmp69*(tmp364 + 2*tmp546) + tmp69*(2*tmp365 + tmp545) + tmp69*(tmp260*tmp286 + tmp543) + tmp69*(tmp333 + tmp417 + tmp541) + tmp69*(tmp262*tmp356 + tmp333 + tmp542) + tmp71*(2*tmp393 + tmp537) + tmp71*(tmp411 + tmp536) + tmp71*(tmp416 + tmp418 + tmp419) + tmp74*(tmp350 + tmp539) + tmp74*(tmp362 + 2*tmp363) + tmp74*(tmp331 + tmp332 + tmp538) + tmp81*(2*tmp387 + tmp548) + tmp81*(tmp402 + tmp549) + tmp81*(tmp413 + tmp532) + tmp81*(tmp426 + tmp531) + tmp81*(tmp533 + tmp547) + tmp81*(tmp396 + tmp397 + tmp498) - tmp82*(tmp463 + tmp553) + tmp98*(tmp343 + 2*tmp440) + tmp98*(tmp347 + tmp550) + tmp98*(tmp433 + tmp551) + tmp98*(tmp442 + tmp529) + tmp98*(tmp445 + tmp528) + tmp98*(tmp324 + tmp448 + tmp540)) - tmp70*tmp71*tmp72 - 8*tmp71*(-tmp104*(-tmp158 - tmp159 - tmp160) + tmp96*(-cf_dDD11 + tmp70*tmp95) - tmp97*(-tmp153 - tmp155 - tmp157)) + tmp71*(-tmp101*(tmp0*tmp299 + tmp374 + tmp377) - tmp101*(hDD_dD111*tmp34 + tmp311*tmp34 + tmp366 + tmp374 - 3*tmp376) - tmp119*(4*hDD11 + hDD_dD110*tmp34 + hDD_dDD1100*tmp0 - 2*tmp379) + tmp127*(tmp236 + 2*tmp395) + tmp127*(tmp290 + 2*tmp394) + tmp15*tmp368 - tmp204*(tmp10*tmp378 + tmp382 + tmp383) + tmp223*tmp387 + tmp223*tmp391 + tmp225*tmp365 + tmp232*tmp386 + tmp232*tmp390 + tmp234*tmp392 + tmp234*tmp393 + tmp237*tmp284*tmp389 + tmp242*tmp288 + tmp296*(tmp181*tmp228 + tmp227*tmp366 + 2*tmp280) + tmp297*(tmp181*tmp283 + tmp285*tmp366 + 2*tmp388) + tmp298*(tmp181*tmp270 + tmp272*tmp366 + 2*tmp358) + tmp367*tmp46 + tmp371*tmp6 + tmp69*(2*tmp410 + tmp411) + tmp69*(tmp418 + 2*tmp419) + tmp69*(tmp268*tmp401 + tmp408) + tmp69*(tmp353*tmp398 + tmp416) + tmp71*(tmp281*tmp401 + tmp283*tmp286) + tmp71*(tmp285*tmp356 + tmp359*tmp398) + tmp74*(tmp333 + 2*tmp417) + tmp74*(tmp352 + 2*tmp409) + tmp81*(2*tmp406 + tmp407) + tmp81*(tmp414 + 2*tmp415) + tmp81*(tmp251*tmp401 + tmp400) + tmp81*(tmp268*tmp398 + tmp399) - tmp82*(-tmp165*tmp195 + tmp381) - tmp82*(-tmp165*tmp184 + tmp190*tmp34 + tmp381 + tmp384 + tmp385) - tmp93*(hDD_dDD1111*tmp0 + tmp0*tmp304 + tmp307*tmp34 + tmp378) + tmp98*(2*tmp396 + tmp397) + tmp98*(tmp402 + 2*tmp403) + tmp98*(2*tmp404 + tmp405) + tmp98*(2*tmp412 + tmp413) - tmp99*(tmp372 - tmp373) - tmp99*(-tmp165*tmp179 + 3*tmp372)) - tmp72*tmp73*tmp74 - 8*tmp74*(-tmp104*(-tmp135 - tmp136 - tmp137) + tmp96*(-cf_dDD00 + tmp73*tmp95) - tmp97*(-tmp131 - tmp132 - tmp134)) + tmp74*(hDD01*lambdaU_dD10 + hDD02*lambdaU_dD20 - hDD_dDD0000*tmp119 + lambdaU_dD00*tmp4 - tmp101*(-tmp299 + tmp301) - tmp101*(tmp163*tmp9 + tmp301 - 2*tmp312) + tmp127*(tmp224 + 2*tmp335) + tmp127*(tmp275 + 2*tmp334) - tmp204*(tmp161*tmp300 + tmp319) + tmp223*tmp323 + tmp223*tmp327 + tmp225*tmp262*tmp326 + tmp232*tmp324 + tmp232*tmp330 + tmp234*tmp331 + tmp234*tmp332 + tmp237*tmp333 + tmp242*tmp259 + tmp296*(tmp217*tmp366 + tmp219*tmp295 + 2*tmp257) + tmp297*(tmp270*tmp295 + tmp271*tmp366 + 2*tmp328) + tmp298*(tmp260*tmp295 + tmp261*tmp366 + 2*tmp325) + tmp69*(2*tmp348 + tmp349) + tmp69*(tmp361 + 2*tmp362) + tmp69*(tmp277*tmp339 + tmp350) + tmp69*(tmp337*tmp356 + tmp363) + tmp71*(2*tmp351 + tmp352) + tmp71*(2*tmp364 + tmp365) + tmp74*(tmp258*tmp339 + tmp260*tmp263) + tmp74*(tmp261*tmp353 + tmp329*tmp337) + tmp81*(tmp340 + 2*tmp341) + tmp81*(tmp342 + 2*tmp343) + tmp81*(2*tmp346 + tmp347) + tmp81*(2*tmp357 + tmp360) - tmp82*(tmp314 + tmp316) - tmp82*(tmp314 - 2*tmp320 + tmp322) - tmp93*(hDD_dDD0011 - tmp304 + tmp309) + tmp98*(2*tmp344 + tmp345) + tmp98*(tmp354 + 2*tmp355) + tmp98*(tmp246*tmp339 + tmp338) + tmp98*(tmp277*tmp337 + tmp336) - tmp99*(4*tmp302 - 2*tmp303) - tmp99*(tmp128*tmp163 - 2*tmp129)) - 16*tmp81*(-tmp104*(-tmp118 - tmp120) - tmp97*(-tmp114 - tmp117)) + tmp81*(tmp127*(2*tmp244 + tmp485) + tmp127*(tmp249 + tmp486) + tmp127*(tmp250 + tmp252 + tmp293) + tmp518 + tmp69*(tmp404 + 2*tmp495) + tmp69*(tmp423 + tmp493) + tmp69*(tmp425 + tmp502) + tmp69*(tmp428 + tmp497) + tmp69*(tmp498 + tmp499) + tmp69*(tmp391 + tmp500 + tmp501) + tmp71*(tmp399 + tmp487) + tmp71*(2*tmp400 + tmp406) + tmp71*(tmp386 + tmp390 + tmp488) + tmp74*(tmp360 + tmp447) + tmp74*(tmp436 + tmp489) + tmp74*(tmp444 + tmp490) + tmp81*(2*tmp236 + tmp492) + tmp81*(tmp290 + tmp503) + tmp81*(tmp395 + 2*tmp496) + tmp81*(tmp246*tmp285 + tmp504) + tmp81*(tmp288 + tmp289 + tmp505) + tmp81*(tmp251*tmp284 + tmp288 + tmp506) - tmp82*(tmp212 + tmp512) - tmp93*(tmp385 + tmp510) + tmp98*(2*tmp233 + tmp491) + tmp98*(tmp265 + tmp507) + tmp98*(tmp282 + tmp455) + tmp98*(tmp439 + tmp494) + tmp98*(tmp452 + tmp509) + tmp98*(tmp269 + tmp274 + tmp508)) + tmp81*(tmp127*(tmp250 + 2*tmp293) + tmp127*(tmp294 + tmp486) + tmp127*(tmp231 + tmp244 + tmp485) + tmp518 + tmp69*(2*tmp391 + tmp501) + tmp69*(tmp413 + tmp499) + tmp69*(tmp424 + tmp532) + tmp69*(tmp427 + tmp531) + tmp69*(tmp402 + tmp493 + tmp533) + tmp69*(tmp404 + tmp405 + tmp495) + tmp71*(2*tmp390 + tmp488) + tmp71*(tmp414 + tmp487) + tmp71*(tmp400 + tmp406 + tmp407) + tmp74*(tmp443 + tmp529) + tmp74*(tmp446 + tmp528) + tmp74*(tmp340 + tmp347 + tmp489) + tmp81*(2*tmp288 + tmp506) + tmp81*(tmp289 + 2*tmp505) + tmp81*(tmp290 + tmp504) + tmp81*(tmp240*tmp356 + tmp503) + tmp81*(tmp236 + tmp395 + tmp496) + tmp81*(tmp215*tmp286 + tmp236 + tmp492) - tmp82*(tmp214 + tmp512) - tmp93*(tmp384 + tmp510) + tmp98*(tmp274 + 2*tmp508) + tmp98*(tmp451 + tmp534) + tmp98*(tmp454 + tmp530) + tmp98*(tmp456 + tmp507) + tmp98*(tmp233 + tmp491 + tmp526) + tmp98*(tmp235 + tmp438 + tmp494)) - 16*tmp98*(-tmp104*(-tmp100 - tmp102) - tmp97*(-tmp83 - tmp94)) + tmp98*(tmp127*(2*tmp243 + tmp420) + tmp127*(tmp254 + tmp421) + tmp127*(tmp245 + tmp247 + tmp267) + tmp484 + tmp69*(tmp346 + 2*tmp433) + tmp69*(tmp435 + tmp436) + tmp69*(tmp440 + tmp441) + tmp69*(tmp442 + tmp444) + tmp69*(tmp445 + tmp447) + tmp69*(tmp330 + tmp448 + tmp449) + tmp71*(tmp413 + tmp425) + tmp71*(tmp422 + tmp423) + tmp71*(tmp426 + tmp428) + tmp74*(tmp336 + tmp429) + tmp74*(2*tmp338 + tmp344) + tmp74*(tmp323 + tmp327 + tmp430) + tmp81*(2*tmp235 + tmp431) + tmp81*(tmp265 + tmp452) + tmp81*(tmp282 + tmp450) + tmp81*(tmp437 + tmp439) + tmp81*(tmp453 + tmp455) + tmp81*(tmp278 + tmp279 + tmp456) - tmp93*(tmp463 + tmp469) + tmp98*(2*tmp224 + tmp432) + tmp98*(tmp275 + tmp458) + tmp98*(tmp335 + 2*tmp434) + tmp98*(tmp251*tmp261 + tmp457) + tmp98*(tmp259 + tmp264 + tmp459) + tmp98*(tmp246*tmp262 + tmp259 + tmp460)) + tmp98*(tmp127*(tmp245 + 2*tmp267) + tmp127*(tmp292 + tmp421) + tmp127*(tmp222 + tmp243 + tmp420) + tmp484 + tmp69*(2*tmp330 + tmp449) + tmp69*(tmp360 + tmp441) + tmp69*(tmp443 + tmp522) + tmp69*(tmp446 + tmp523) + tmp69*(tmp340 + tmp435 + tmp524) + tmp69*(tmp346 + tmp347 + tmp433) + tmp71*(tmp424 + tmp519) + tmp71*(tmp427 + tmp520) + tmp71*(tmp402 + tmp405 + tmp422) + tmp74*(2*tmp327 + tmp430) + tmp74*(tmp354 + tmp429) + tmp74*(tmp338 + tmp344 + tmp345) + tmp81*(tmp279 + 2*tmp456) + tmp81*(tmp450 + tmp508) + tmp81*(tmp451 + tmp521) + tmp81*(tmp454 + tmp525) + tmp81*(tmp233 + tmp437 + tmp438) + tmp81*(tmp235 + tmp431 + tmp526) - tmp93*(tmp469 + tmp527) + tmp98*(2*tmp259 + tmp460) + tmp98*(tmp264 + 2*tmp459) + tmp98*(tmp275 + tmp457) + tmp98*(tmp239*tmp353 + tmp458) + tmp98*(tmp224 + tmp335 + tmp434) + tmp98*(tmp215*tmp263 + tmp224 + tmp432))) + (2.0/3.0)*((trK)*(trK));
}
} // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++)
} // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++)
} // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++)
}
// Step P0: Define REAL and NGHOSTS. This header is generated by NRPy+.
#include "Initial_Data_Playground_REAL__NGHOSTS.h"
#include "declare_Cparameters_struct.h"
// Step P1: Import needed header files
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#ifndef M_PI
#define M_PI 3.141592653589793238462643383279502884L
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524400844362104849039L
#endif
// Step P2: Declare the IDX4S(gf,i,j,k) macro, which enables us to store 4-dimensions of
// data in a 1D array. In this case, consecutive values of "i"
// (all other indices held to a fixed value) are consecutive in memory, where
// consecutive values of "j" (fixing all other indices) are separated by
// Nxx_plus_2NGHOSTS0 elements in memory. Similarly, consecutive values of
// "k" are separated by Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1 in memory, etc.
#define IDX4S(g,i,j,k) ( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) + Nxx_plus_2NGHOSTS2 * (g) ) ) )
#define IDX4ptS(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2) * (g) )
#define IDX3S(i,j,k) ( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) ) ) )
#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)
#define LOOP_ALL_GFS_GPS(ii) _Pragma("omp parallel for") for(int (ii)=0;(ii)<Nxx_plus_2NGHOSTS_tot*NUM_EVOL_GFS;(ii)++)
// Step P3: Set UUGF and VVGF macros, as well as xxCart()
#include "boundary_conditions/gridfunction_defines.h"
// Step P4: Set xxCart(const paramstruct *restrict params,
// REAL *restrict xx[3],
// const int i0,const int i1,const int i2,
// REAL xCart[3]),
// which maps xx->Cartesian via
// {xx[0][i0],xx[1][i1],xx[2][i2]}->{xCart[0],xCart[1],xCart[2]}
#include "xxCart.h"
// Step P5: Defines set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3],
// paramstruct *restrict params, REAL *restrict xx[3]),
// which sets params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for
// the chosen Eigen-CoordSystem if EigenCoord==1, or
// CoordSystem if EigenCoord==0.
#include "set_Nxx_dxx_invdx_params__and__xx.h"
// Step P6: Include basic functions needed to impose curvilinear
// parity and boundary conditions.
#include "boundary_conditions/CurviBC_include_Cfunctions.h"
// Step P8: Include function for enforcing detgammabar constraint.
#include "enforce_detgammabar_constraint.h"
// Step P10: Declare function necessary for setting up the initial data.
// Step P10.a: Define BSSN_ID() for BrillLindquist initial data
// Step P10.b: Set the generic driver function for setting up BSSN initial data
#include "initial_data.h"
// Step P11: Declare function for evaluating Hamiltonian constraint (diagnostic)
#include "Hamiltonian_constraint.h"
#include "momentum_constraint.h"
// main() function:
// Step 0: Read command-line input, set up grid structure, allocate memory for gridfunctions, set up coordinates
// Step 1: Set up initial data to an exact solution
// Step 2: Start the timer, for keeping track of how fast the simulation is progressing.
// Step 3: Integrate the initial data forward in time using the chosen RK-like Method of
// Lines timestepping algorithm, and output periodic simulation diagnostics
// Step 3.a: Output 2D data file periodically, for visualization
// Step 3.b: Step forward one timestep (t -> t+dt) in time using
// chosen RK-like MoL timestepping algorithm
// Step 3.c: If t=t_final, output conformal factor & Hamiltonian
// constraint violation to 2D data file
// Step 3.d: Progress indicator printing to stderr
// Step 4: Free all allocated memory
int main(int argc, const char *argv[]) {
paramstruct params;
#include "set_Cparameters_default.h"
// Step 0a: Read command-line input, error out if nonconformant
if((argc != 4) || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < 2 /* FIXME; allow for axisymmetric sims */) {
fprintf(stderr,"Error: Expected three command-line arguments: ./BrillLindquist_Playground Nx0 Nx1 Nx2,
");
fprintf(stderr,"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.
");
fprintf(stderr,"Nx[] MUST BE larger than NGHOSTS (= %d)
",NGHOSTS);
exit(1);
}
// Step 0b: Set up numerical grid structure, first in space...
const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };
if(Nxx[0]%2 != 0 || Nxx[1]%2 != 0 || Nxx[2]%2 != 0) {
fprintf(stderr,"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.
");
fprintf(stderr," For example, in case of angular directions, proper symmetry zones will not exist.
");
exit(1);
}
// Step 0c: Set free parameters, overwriting Cparameters defaults
// by hand or with command-line input, as desired.
#include "free_parameters.h"
// Step 0d: Uniform coordinate grids are stored to *xx[3]
REAL *xx[3];
// Step 0d.i: Set bcstruct
bc_struct bcstruct;
{
int EigenCoord = 1;
// Step 0d.ii: Call set_Nxx_dxx_invdx_params__and__xx(), which sets
// params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the
// chosen Eigen-CoordSystem.
set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);
// Step 0d.iii: Set Nxx_plus_2NGHOSTS_tot
#include "set_Cparameters-nopointer.h"
const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;
// Step 0e: Find ghostzone mappings; set up bcstruct
#include "boundary_conditions/driver_bcstruct.h"
// Step 0e.i: Free allocated space for xx[][] array
for(int i=0;i<3;i++) free(xx[i]);
}
// Step 0f: Call set_Nxx_dxx_invdx_params__and__xx(), which sets
// params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the
// chosen (non-Eigen) CoordSystem.
int EigenCoord = 0;
set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);
// Step 0g: Set all C parameters "blah" for params.blah, including
// Nxx_plus_2NGHOSTS0 = params.Nxx_plus_2NGHOSTS0, etc.
#include "set_Cparameters-nopointer.h"
const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;
// Step 0j: Error out if the number of auxiliary gridfunctions outnumber evolved gridfunctions.
// This is a limitation of the RK method. You are always welcome to declare & allocate
// additional gridfunctions by hand.
if(NUM_AUX_GFS > NUM_EVOL_GFS) {
fprintf(stderr,"Error: NUM_AUX_GFS > NUM_EVOL_GFS. Either reduce the number of auxiliary gridfunctions,
");
fprintf(stderr," or allocate (malloc) by hand storage for *diagnostic_output_gfs.
");
exit(1);
}
// Step 0k: Allocate memory for gridfunctions
#include "MoLtimestepping/RK_Allocate_Memory.h"
REAL *restrict auxevol_gfs = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot);
// Step 1: Set up initial data to an exact solution
initial_data(¶ms, xx, y_n_gfs);
// Step 1b: Apply boundary conditions, as initial data
// are sometimes ill-defined in ghost zones.
// E.g., spherical initial data might not be
// properly defined at points where r=-1.
apply_bcs_curvilinear(¶ms, &bcstruct, NUM_EVOL_GFS,evol_gf_parity, y_n_gfs);
enforce_detgammabar_constraint(¶ms, xx, y_n_gfs);
// Evaluate Hamiltonian & momentum constraint violations
Hamiltonian_constraint(¶ms, xx, y_n_gfs, diagnostic_output_gfs);
momentum_constraint( ¶ms, xx, y_n_gfs, diagnostic_output_gfs);
/* Step 2: 2D output: Output conformal factor (CFGF) and constraint violations (HGF, MU0GF, MU1GF, MU2GF). */
const int i0MIN=NGHOSTS; // In spherical, r=Delta r/2.
const int i1mid=Nxx_plus_2NGHOSTS1/2;
const int i2mid=Nxx_plus_2NGHOSTS2/2;
LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS0-NGHOSTS, i1mid,i1mid+1, NGHOSTS,Nxx_plus_2NGHOSTS2-NGHOSTS) {
REAL xCart[3];
xxCart(¶ms, xx, i0,i1,i2, xCart);
int idx = IDX3S(i0,i1,i2);
printf("%e %e %e %e %e %e %e
",xCart[0],xCart[1], y_n_gfs[IDX4ptS(CFGF,idx)],
log10(fabs(diagnostic_output_gfs[IDX4ptS(HGF,idx)])),
log10(fabs(diagnostic_output_gfs[IDX4ptS(MU0GF,idx)])+1e-200),
log10(fabs(diagnostic_output_gfs[IDX4ptS(MU1GF,idx)])+1e-200),
log10(fabs(diagnostic_output_gfs[IDX4ptS(MU2GF,idx)])+1e-200));
}
// Step 4: Free all allocated memory
#include "boundary_conditions/bcstruct_freemem.h"
#include "MoLtimestepping/RK_Free_Memory.h"
free(auxevol_gfs);
for(int i=0;i<3;i++) free(xx[i]);
return 0;
}
// Part P0.a: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER
#define NGHOSTS 3
// Part P0.b: Set the numerical precision (REAL) to double, ensuring all floating point
// numbers are stored to at least ~16 significant digits
#define REAL double
// Code snippet allocating gridfunction memory for "Euler" method:
REAL *restrict y_n_gfs = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot);
REAL *restrict y_nplus1_running_total_gfs = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot);
REAL *restrict diagnostic_output_gfs = y_nplus1_running_total_gfs;
// Code snippet freeing gridfunction memory for "Euler" method:
free(y_n_gfs);
free(y_nplus1_running_total_gfs);
// C code implementation of Euler Method of Lines timestepping.
// ***Euler timestepping only requires one RHS evaluation***
LOOP_ALL_GFS_GPS(i) {
y_n_gfs[i] = y_n_gfs[i] + y_nplus1_running_total_gfs[i]*dt;
}
typedef struct __ghostzone_map__ {
short i0,i1,i2; // i0,i1,i2 stores values from -1 (used to indicate outer boundary)
// to Nxx_plus_2NGHOSTS*. We assume that grid extents beyond the
// limits of short (i.e., beyond about 32,000) are unlikely. This
// can be easily extended if needed, though.
} gz_map;
const int8_t MAXFACE = -1;
const int8_t NUL = +0;
const int8_t MINFACE = +1;
typedef struct __parity__ {
int8_t parity[10]; // We store the 10 parity conditions in 10 int8_t integers,
// one for each condition. Note that these conditions can
// only take one of two values: +1 or -1, hence the use of
// int8_t, the smallest C data type.
} parity_condition;
typedef struct __inner_bc__ {
gz_map inner_bc_dest_pt;
gz_map inner_bc_src_pt;
int8_t parity[10]; // We store the 10 parity conditions in 10 int8_t integers,
// one for each condition. Note that these conditions can
// only take one of two values: +1 or -1, hence the use of
// int8_t, the smallest C data type.
} inner_bc;
typedef struct __outer_bc__ {
gz_map outer_bc_dest_pt;
int8_t FACEi0,FACEi1,FACEi2; // FACEi* takes values of -1, 0, and +1 only,
// corresponding to MAXFACE, NUL, and MINFACE
// respectively.
// Thus int8_t (one byte each, the smallest C
// type) is sufficient.
} outer_bc;
typedef struct __bcstruct__ {
outer_bc **outer; // Array of 1D arrays, of length
// [NGHOSTS][num_ob_gz_pts[which_outer_ghostzone_point]]
inner_bc **inner; // Array of 1D arrays, of length
// [NGHOSTS][num_ib_gz_pts[which_inner_ghostzone_point]]
// Arrays storing number of outer/inner boundary ghostzone points at each ghostzone,
// of length NGHOSTS:
int *num_ob_gz_pts;
int *num_ib_gz_pts;
} bc_struct;
#include "BCs_data_structs.h"
#include "EigenCoord_xxCart.h"
#include "set_up__bc_gz_map_and_parity_condns.h"
#include "set_bcstruct.h"
#include "apply_bcs_curvilinear.h"
/*
* Original SymPy expressions:
* "[Cart_to_xx0_inbounds = sqrt(Cartx**2 + Carty**2 + Cartz**2),
* Cart_to_xx1_inbounds = acos(Cartz/sqrt(Cartx**2 + Carty**2 + Cartz**2)),
* Cart_to_xx2_inbounds = atan2(Carty, Cartx)]"
*/
{
const double tmp0 = sqrt(((Cartx)*(Cartx)) + ((Carty)*(Carty)) + ((Cartz)*(Cartz)));
Cart_to_xx0_inbounds = tmp0;
Cart_to_xx1_inbounds = acos(Cartz/tmp0);
Cart_to_xx2_inbounds = atan2(Carty, Cartx);
}
inline void EigenCoord_xxCart(const paramstruct *restrict params, REAL *restrict xx[3],const int i0,const int i1,const int i2, REAL xCart[3]) {
#include "../set_Cparameters.h"
REAL xx0 = xx[0][i0];
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];
/*
* Original SymPy expressions:
* "[xCart[0] = xx0*sin(xx1)*cos(xx2),
* xCart[1] = xx0*sin(xx1)*sin(xx2),
* xCart[2] = xx0*cos(xx1)]"
*/
{
const double tmp0 = xx0*sin(xx1);
xCart[0] = tmp0*cos(xx2);
xCart[1] = tmp0*sin(xx2);
xCart[2] = xx0*cos(xx1);
}
}
#include "..//set_Cparameters.h"
// Declare boundary condition BC_UPDATE_OUTER macro,
// which updates a single outer boundary face
// of the 3D grid cube using quadratic polynomial
// extrapolation.
#define BC_UPDATE_OUTER(which_gf, i0,i1,i2, FACEX0,FACEX1,FACEX2) { \
const int idx3 = IDX3S(i0,i1,i2); \
gfs[IDX4S(which_gf,i0,i1,i2)] = \
+3.0*gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)] \
-3.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)]; \
}
// Curvilinear boundary condition driver routine: Apply BCs to all six
// boundary faces of the 3D numerical domain, filling in the
// innermost ghost zone layer first, and moving outward.
void apply_bcs_curvilinear(const paramstruct *restrict params, const bc_struct *restrict bcstruct,
const int NUM_GFS, const int8_t *restrict gfs_parity, REAL *restrict gfs) {
#pragma omp parallel for
for(int which_gf=0;which_gf<NUM_GFS;which_gf++) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// First apply OUTER boundary conditions,
// in case an INNER (parity) boundary point
// needs data at the outer boundary:
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
for(int pt=0;pt<bcstruct->num_ob_gz_pts[which_gz];pt++) {
BC_UPDATE_OUTER(which_gf,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i0,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i1,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i2,
bcstruct->outer[which_gz][pt].FACEi0,
bcstruct->outer[which_gz][pt].FACEi1,
bcstruct->outer[which_gz][pt].FACEi2);
}
// Then apply INNER (parity) boundary conditions:
for(int pt=0;pt<bcstruct->num_ib_gz_pts[which_gz];pt++) {
const int i0dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i0;
const int i1dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i1;
const int i2dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i2;
const int i0src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i0;
const int i1src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i1;
const int i2src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i2;
const int8_t *prty= bcstruct->inner[which_gz][pt].parity;
// printf("%d\n",bcstruct->inner_bc_parity[which_gz][pt].parity[gfs_parity[which_gf]]);
gfs[IDX4S(which_gf,i0dest,i1dest,i2dest)] =
bcstruct->inner[which_gz][pt].parity[gfs_parity[which_gf]] * gfs[IDX4S(which_gf, i0src,i1src,i2src)];
} // END for(int pt=0;pt<num_ib_gz_pts[which_gz];pt++)
} // END for(int which_gz = 0; which_gz < NGHOSTS; which_gz++)
} // END for(int which_gf=0;which_gf<NUM_GFS;which_gf++)
} // END function
for(int i=0;i<NGHOSTS;i++) { free(bcstruct.outer[i]); free(bcstruct.inner[i]); }
free(bcstruct.num_ob_gz_pts); free(bcstruct.num_ib_gz_pts);
// Step 1: Allocate memory storage for bc_gz_map, which
// in the case a boundary point is a *parity*
// boundary, is set to the interior, non-
// boundary point corresponding to the same
// Cartesian gridpoint. Otherwise bc_gz_map
// is set to (i0,i1,i2) = (-1,-1,-1).
gz_map *bc_gz_map = (gz_map *)malloc(sizeof(gz_map)*Nxx_plus_2NGHOSTS_tot);
// Step 2: Allocate memory storage for bc_parity_conditions,
// which store parity conditions for all 10
// gridfunction types at all grid points.
parity_condition *bc_parity_conditions = (parity_condition *)malloc(sizeof(parity_condition)*Nxx_plus_2NGHOSTS_tot);
// Step 3: Set bc_gz_map and bc_parity_conditions at *all*
// points; on the boundary and otherwise.
set_up__bc_gz_map_and_parity_condns(¶ms, xx, bc_gz_map,
bc_parity_conditions
);
// Step 4: Declare and allocate memory for bcstruct,
// which will store all information needed for
// applying the boundary conditions.
bcstruct.outer = (outer_bc **)malloc(sizeof(outer_bc *)*NGHOSTS);
bcstruct.inner = (inner_bc **)malloc(sizeof(inner_bc *)*NGHOSTS);
bcstruct.num_ob_gz_pts = ( int *)malloc(sizeof(int)*NGHOSTS);
bcstruct.num_ib_gz_pts = ( int *)malloc(sizeof(int)*NGHOSTS);
// Step 4: Store all information needed to quickly and
// efficiently apply boundary conditions. This
// function transfers all information from
// bc_gz_map (defined at *all gridpoints*) into
// bcstruct (defined only at boundary points).
// Thus when this function has finished,
// bc_gz_map is no longer needed.
set_bcstruct(¶ms,bc_gz_map,
bc_parity_conditions,
&bcstruct);
// Step 5: As described in Step 4, bc_gz_map is no
// longer needed at this point, so we free its
// memory. Farewell, friend!
free(bc_gz_map);
free(bc_parity_conditions);
/* This file is automatically generated by NRPy+. Do not edit. */
/* EVOLVED VARIABLES: */
#define NUM_EVOL_GFS 24
#define ADD00GF 0
#define ADD01GF 1
#define ADD02GF 2
#define ADD11GF 3
#define ADD12GF 4
#define ADD22GF 5
#define ALPHAGF 6
#define BETU0GF 7
#define BETU1GF 8
#define BETU2GF 9
#define CFGF 10
#define HDD00GF 11
#define HDD01GF 12
#define HDD02GF 13
#define HDD11GF 14
#define HDD12GF 15
#define HDD22GF 16
#define LAMBDAU0GF 17
#define LAMBDAU1GF 18
#define LAMBDAU2GF 19
#define TRKGF 20
#define VETU0GF 21
#define VETU1GF 22
#define VETU2GF 23
/* AUXILIARY VARIABLES: */
#define NUM_AUX_GFS 4
#define HGF 0
#define MU0GF 1
#define MU1GF 2
#define MU2GF 3
/* AUXEVOL VARIABLES: */
#define NUM_AUXEVOL_GFS 0
/* PARITY TYPES FOR ALL GRIDFUNCTIONS.
SEE "Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb" FOR DEFINITIONS. */
const int8_t evol_gf_parity[24] = { 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 1, 2, 3, 0, 1, 2, 3 };
const int8_t aux_gf_parity[4] = { 0, 1, 2, 3 };
/*
* Original SymPy expressions:
* "[parity[0] = 1,
* parity[1] = sin(xx1)*sin(xx1_inbounds)*sin(xx2)*sin(xx2_inbounds) + sin(xx1)*sin(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds) + cos(xx1)*cos(xx1_inbounds),
* parity[2] = sin(xx1)*sin(xx1_inbounds) + sin(xx2)*sin(xx2_inbounds)*cos(xx1)*cos(xx1_inbounds) + cos(xx1)*cos(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds),
* parity[3] = sin(xx2)*sin(xx2_inbounds) + cos(xx2)*cos(xx2_inbounds),
* parity[4] = (sin(xx1)*sin(xx1_inbounds)*sin(xx2)*sin(xx2_inbounds) + sin(xx1)*sin(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds) + cos(xx1)*cos(xx1_inbounds))**2,
* parity[5] = (sin(xx1)*sin(xx1_inbounds) + sin(xx2)*sin(xx2_inbounds)*cos(xx1)*cos(xx1_inbounds) + cos(xx1)*cos(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds))*(sin(xx1)*sin(xx1_inbounds)*sin(xx2)*sin(xx2_inbounds) + sin(xx1)*sin(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds) + cos(xx1)*cos(xx1_inbounds)),
* parity[6] = (sin(xx2)*sin(xx2_inbounds) + cos(xx2)*cos(xx2_inbounds))*(sin(xx1)*sin(xx1_inbounds)*sin(xx2)*sin(xx2_inbounds) + sin(xx1)*sin(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds) + cos(xx1)*cos(xx1_inbounds)),
* parity[7] = (sin(xx1)*sin(xx1_inbounds) + sin(xx2)*sin(xx2_inbounds)*cos(xx1)*cos(xx1_inbounds) + cos(xx1)*cos(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds))**2,
* parity[8] = (sin(xx2)*sin(xx2_inbounds) + cos(xx2)*cos(xx2_inbounds))*(sin(xx1)*sin(xx1_inbounds) + sin(xx2)*sin(xx2_inbounds)*cos(xx1)*cos(xx1_inbounds) + cos(xx1)*cos(xx1_inbounds)*cos(xx2)*cos(xx2_inbounds)),
* parity[9] = (sin(xx2)*sin(xx2_inbounds) + cos(xx2)*cos(xx2_inbounds))**2]"
*/
{
const double tmp0 = cos(xx1)*cos(xx1_inbounds);
const double tmp1 = sin(xx1)*sin(xx1_inbounds);
const double tmp2 = sin(xx2)*sin(xx2_inbounds);
const double tmp3 = cos(xx2)*cos(xx2_inbounds);
const double tmp4 = tmp0 + tmp1*tmp2 + tmp1*tmp3;
const double tmp5 = tmp0*tmp2 + tmp0*tmp3 + tmp1;
const double tmp6 = tmp2 + tmp3;
parity[0] = 1;
parity[1] = tmp4;
parity[2] = tmp5;
parity[3] = tmp6;
parity[4] = ((tmp4)*(tmp4));
parity[5] = tmp4*tmp5;
parity[6] = tmp4*tmp6;
parity[7] = ((tmp5)*(tmp5));
parity[8] = tmp5*tmp6;
parity[9] = ((tmp6)*(tmp6));
}
// set_bcstruct() loops from the innermost boundary
// ghostzones on the cube ("which_gz==0",
// corresponding to the single layer of ghostzones
// closest to the interior data), and at each
// ghostzone layer, we apply the following 5-step
// algorithm:
// Step 1: Count the number of outer and inner
// boundary points, store to
// num_ob_pts and num_ib_pts, respectively.
// Step 2: Now that we know the number of outer
// boundary points on this ghostzone layer,
// allocate memory needed for storing the
// outer and inner boundary condition data.
// Step 2.a: At all outer boundary ghost zones, allocate
// memory for a single member of the outer_bc
// data type.
// Step 2.b: At all inner boundary ghost zones, allocate
// memory for a single member of the inner_bc
// data type.
// Step 3: Store the number of outer and inner boundary
// points on each ghostzone layer, where e.g.,
// which_gz==0 corresponds to the innermost
// ghostzones on the numerical domain.
// Step 4: Store information needed for outer boundary
// conditions, to outer_bc_dest_pt and
// outer_bc_face arrays.
// Step 5: Store information needed for inner boundary
// conditions, including interior point to which
// inner ghost zone maps, and parity conditions
// for all 10 gridfunction types.
void set_bcstruct(const paramstruct *restrict params,
gz_map *restrict bc_gz_map,
parity_condition *bc_parity_conditions,
bc_struct *restrict bcstruct) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
// Loop from the innermost ghostzone on the cube (which_gz==0) and work outward.
// This ordering is necessary, as ghostzones at which_gz==1 will generally
// depend on ghostzones at which_gz==0 being already set.
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// Step 1: Count the number of outer and inner
// boundary points, store to
// num_ob_pts and num_ib_pts, respectively.
#define COUNT_INNER_OR_OUTER if(bc_gz_map[IDX3S(i0,i1,i2)].i0==-1) { num_ob_pts++;} else { num_ib_pts++; }
int num_ob_pts = 0;
int num_ib_pts = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) { COUNT_INNER_OR_OUTER } imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) { COUNT_INNER_OR_OUTER } imax[2]++;
// Step 2: Now that we know the number of outer boundary points on this ghostzone
// layer, we allocate memory needed for storing the outer and inner boundary
// condition data.
// Step 2.a: At all outer boundary ghost zones, allocate memory for a single member of the outer_bc
// data type.
bcstruct->outer[which_gz] = (outer_bc *)malloc(sizeof(outer_bc)*num_ob_pts);
// Step 2.b: At all inner boundary ghost zones, allocate memory for a single member of the inner_bc
// data type.
bcstruct->inner[which_gz] = (inner_bc *)malloc(sizeof(inner_bc)*num_ib_pts);
// Step 3: Store the number of outer and inner boundary points on each ghostzone layer, where e.g.,
// which_gz==0 corresponds to the innermost ghostzones on the numerical domain.
bcstruct->num_ob_gz_pts[which_gz] = num_ob_pts;
bcstruct->num_ib_gz_pts[which_gz] = num_ib_pts;
// Reset imin[] and imax[], to prepare for the next step.
for(int ii=0;ii<3;ii++) {imin[ii]++; imax[ii]--;}
// Step 4: Store information needed for outer boundary conditions, to outer_bc_dest_pt[which_gz][]
// and outer_bc_face[which_gz][] arrays:
#define OB_SET(facei0,facei1,facei2) if(bc_gz_map[IDX3S(i0,i1,i2)].i0==-1) { \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i0 = i0; \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i1 = i1; \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i2 = i2; \
bcstruct->outer[which_gz][pt].FACEi0= facei0; \
bcstruct->outer[which_gz][pt].FACEi1= facei1; \
bcstruct->outer[which_gz][pt].FACEi2= facei2; \
pt++; }
int pt = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) {OB_SET(MINFACE,NUL,NUL)} imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) {OB_SET(MAXFACE,NUL,NUL)} imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) {OB_SET(NUL,MINFACE,NUL)} imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) {OB_SET(NUL,MAXFACE,NUL)} imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) {OB_SET(NUL,NUL,MINFACE)} imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) {OB_SET(NUL,NUL,MAXFACE)} imax[2]++;
// fprintf(stderr,"num OB points with which_gz = %d: %d | should be: %d\n",which_gz,pt,num_ob_gz_pts[which_gz]);
// Reset imin[] and imax[], to prepare for the next step.
for(int ii=0;ii<3;ii++) {imin[ii]++; imax[ii]--;}
// Step 5: Store information needed for inner boundary conditions, including interior point to which
// inner ghost zone maps, and parity conditions for all 10 gridfunction types.
#define IB_SET if(bc_gz_map[IDX3S(i0,i1,i2)].i0!=-1) { \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i0=i0; \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i1=i1; \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i2=i2; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i0 =bc_gz_map[IDX3S(i0,i1,i2)].i0; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i1 =bc_gz_map[IDX3S(i0,i1,i2)].i1; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i2 =bc_gz_map[IDX3S(i0,i1,i2)].i2; \
for(int ii=0;ii<10;ii++) { \
bcstruct->inner[which_gz][pt].parity[ii] = \
(int8_t)bc_parity_conditions[IDX3S(i0,i1,i2)].parity[ii]; } \
pt++; }
pt = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) {IB_SET} imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) {IB_SET} imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) {IB_SET} imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) {IB_SET} imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) {IB_SET} imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) {IB_SET} imax[2]++;
} // END for(int which_gz = 0; which_gz < NGHOSTS; which_gz++)
} // END function
// set_parity_conditions_from_symbolic_dot_products():
// Evaluate dot products needed for setting parity
// conditions at a given point (xx0,xx1,xx2),
// using C code generated by NRPy+ function
// parity_conditions_symbolic_dot_products().
void eval_symbolic_dot_products_to_set_parity_conditions(const paramstruct *restrict params, REAL parity[10],
const REAL xx0,const REAL xx1,const REAL xx2,
const REAL xx0_inbounds,const REAL xx1_inbounds,const REAL xx2_inbounds) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
#include "parity_conditions_symbolic_dot_products.h"
}
void set_up__bc_gz_map_and_parity_condns(const paramstruct *restrict params,
REAL *xx[3], gz_map *bc_gz_map,parity_condition *bc_parity_conditions) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
// xx[0][j] = xxmin[0] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*dxx0;
// -> xxmin[0] = xx[0][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx0
const REAL xxmin[3] = { xx[0][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx0,
xx[1][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx1,
xx[2][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx2 };
//fprintf(stderr,"hey inside setbc: %e %e %e | %e %e\n",xxmin[0],xxmin[1],xxmin[2],xx[0][0],dxx0);
LOOP_REGION(0,Nxx_plus_2NGHOSTS0,0,Nxx_plus_2NGHOSTS1,0,Nxx_plus_2NGHOSTS2) {
// Step 1: Convert the (curvilinear) coordinate (x0,x1,x2) to Cartesian coordinates
REAL xCart[3];
EigenCoord_xxCart(params, xx, i0,i1,i2, xCart);
REAL Cartx = xCart[0];
REAL Carty = xCart[1];
REAL Cartz = xCart[2];
// Step 2: Find the (i0_inbounds,i1_inbounds,i2_inbounds) corresponding to the above Cartesian coordinate.
// If (i0_inbounds,i1_inbounds,i2_inbounds) is in a ghost zone, then it must equal (i0,i1,i2), and
// the point is an outer boundary point.
// Otherwise (i0_inbounds,i1_inbounds,i2_inbounds) is in the grid interior, and data at (i0,i1,i2)
// must be replaced with data at (i0_inbounds,i1_inbounds,i2_inbounds), but multiplied by the
// appropriate parity condition (+/- 1).
REAL Cart_to_xx0_inbounds,Cart_to_xx1_inbounds,Cart_to_xx2_inbounds;
#include "EigenCoord_Cart_to_xx.h"
int i0_inbounds = (int)( (Cart_to_xx0_inbounds - xxmin[0] - (1.0/2.0)*dxx0 + ((REAL)NGHOSTS)*dxx0)/dxx0 + 0.5 );
int i1_inbounds = (int)( (Cart_to_xx1_inbounds - xxmin[1] - (1.0/2.0)*dxx1 + ((REAL)NGHOSTS)*dxx1)/dxx1 + 0.5 );
int i2_inbounds = (int)( (Cart_to_xx2_inbounds - xxmin[2] - (1.0/2.0)*dxx2 + ((REAL)NGHOSTS)*dxx2)/dxx2 + 0.5 );
// Step 2.a: (Sanity/validation check) Convert the interior point
// x0(i0_inbounds),x1(i1_inbounds),x2(i2_inbounds) to Cartesian coordinates,
// make sure that the Cartesian coordinate matches the Cartesian coordinate of
// x0(i0),x1(i1),x2(i2). If not, error out!
REAL xCart_orig[3]; for(int ii=0;ii<3;ii++) xCart_orig[ii] = xCart[ii];
EigenCoord_xxCart(params, xx, i0_inbounds,i1_inbounds,i2_inbounds, xCart);
//fprintf(stderr,"Cartesian agreement: ( %.15e %.15e %.15e ) ?= ( %.15e %.15e %.15e )\n",
// (double)xCart_orig[0],(double)xCart_orig[1],(double)xCart_orig[2],
// (double)xCart[0],(double)xCart[1],(double)xCart[2]);
#define EPS_ABS 1e-8
if(fabs( (double)(xCart_orig[0] - xCart[0]) ) > EPS_ABS ||
fabs( (double)(xCart_orig[1] - xCart[1]) ) > EPS_ABS ||
fabs( (double)(xCart_orig[2] - xCart[2]) ) > EPS_ABS) {
fprintf(stderr,"Error. Cartesian disagreement: ( %.15e %.15e %.15e ) != ( %.15e %.15e %.15e )\n",
(double)xCart_orig[0],(double)xCart_orig[1],(double)xCart_orig[2],
(double)xCart[0],(double)xCart[1],(double)xCart[2]);
exit(1);
}
// Step 3: Set bc_gz_map and bc_parity_conditions.
if(i0_inbounds-i0 == 0 && i1_inbounds-i1 == 0 && i2_inbounds-i2 == 0) {
// Step 3.a: Iff we are on an outer boundary point or in the grid
// interior, i0_inbounds==i0, i1_inbounds==i1, and
// i2_inbounds==i2, and inner boundary conditions do not
// apply: set bc_gz_map to -1, and parity=1.
bc_gz_map[IDX3S(i0,i1,i2)].i0=-1;
bc_gz_map[IDX3S(i0,i1,i2)].i1=-1;
bc_gz_map[IDX3S(i0,i1,i2)].i2=-1;
for(int which_parity=0; which_parity<10; which_parity++) {
bc_parity_conditions[IDX3S(i0,i1,i2)].parity[which_parity] = 1;
}
} else {
// Step 3.b: If we are on an *inner* boundary point:
// 1. Set bc_gz_map at (i0,i1,i2) to the point
// in the interior to which this boundary
// point maps, and
// 2. Perform the unit vector dot products
// necessary to set all 10 possible parity
// conditions, calling function
// set_parity_from_unit_vector_dot_product()
bc_gz_map[IDX3S(i0,i1,i2)].i0=i0_inbounds;
bc_gz_map[IDX3S(i0,i1,i2)].i1=i1_inbounds;
bc_gz_map[IDX3S(i0,i1,i2)].i2=i2_inbounds;
const REAL xx0 = xx[0][i0];
const REAL xx1 = xx[1][i1];
const REAL xx2 = xx[2][i2];
const REAL xx0_inbounds = xx[0][i0_inbounds];
const REAL xx1_inbounds = xx[1][i1_inbounds];
const REAL xx2_inbounds = xx[2][i2_inbounds];
REAL REAL_parity_array[10];
eval_symbolic_dot_products_to_set_parity_conditions(params, REAL_parity_array, xx0,xx1,xx2,
xx0_inbounds,xx1_inbounds,xx2_inbounds);
for(int whichparity=0;whichparity<10;whichparity++) {
//printf("Good? Parity %d evaluated to %e\n",whichparity,(double)REAL_parity_array[whichparity]);
// Perform sanity check on parity array output: should be +1 or -1 to within 8 significant digits:
if( (REAL_parity_array[whichparity] > 0 && fabs(REAL_parity_array[whichparity] - (+1)) > 1e-8) ||
(REAL_parity_array[whichparity] <= 0 && fabs(REAL_parity_array[whichparity] - (-1)) > 1e-8) ) {
fprintf(stderr,"Error. Parity evaluated to %e , which is not within 8 significant digits of +1 or -1.",
REAL_parity_array[whichparity]);
exit(1);
}
if(REAL_parity_array[whichparity] < 0.0) bc_parity_conditions[IDX3S(i0,i1,i2)].parity[whichparity] = -1;
if(REAL_parity_array[whichparity] > 0.0) bc_parity_conditions[IDX3S(i0,i1,i2)].parity[whichparity] = +1;
}
}
}
}
typedef struct __paramstruct__ {
int Nxx0;
int Nxx1;
int Nxx2;
int Nxx_plus_2NGHOSTS0;
int Nxx_plus_2NGHOSTS1;
int Nxx_plus_2NGHOSTS2;
REAL xx0;
REAL xx1;
REAL xx2;
REAL dxx0;
REAL dxx1;
REAL dxx2;
REAL invdx0;
REAL invdx1;
REAL invdx2;
REAL f0_of_xx0;
REAL f1_of_xx1;
REAL f2_of_xx0_xx1;
REAL f3_of_xx0;
REAL f4_of_xx2;
REAL f0_of_xx0__D0;
REAL f0_of_xx0__DD00;
REAL f0_of_xx0__DDD000;
REAL f1_of_xx1__D1;
REAL f1_of_xx1__DD11;
REAL f1_of_xx1__DDD111;
REAL f2_of_xx0_xx1__D0;
REAL f2_of_xx0_xx1__D1;
REAL f2_of_xx0_xx1__DD00;
REAL f2_of_xx0_xx1__DD11;
REAL f3_of_xx0__D0;
REAL f3_of_xx0__DD00;
REAL f4_of_xx2__D2;
REAL f4_of_xx2__DD22;
REAL RMAX;
REAL M;
REAL a;
REAL r0;
} paramstruct;
/*
Enforce det(gammabar) = det(gammahat) constraint.
*/
void enforce_detgammabar_constraint(const paramstruct *restrict params, REAL *restrict xx[3], REAL *restrict in_gfs) {
#include "set_Cparameters.h"
#pragma omp parallel for
for(int i2=0; i2<Nxx_plus_2NGHOSTS2; i2++) {
const REAL xx2 = xx[2][i2];
for(int i1=0; i1<Nxx_plus_2NGHOSTS1; i1++) {
const REAL xx1 = xx[1][i1];
for(int i0=0; i0<Nxx_plus_2NGHOSTS0; i0++) {
const REAL xx0 = xx[0][i0];
/*
* NRPy+ Finite Difference Code Generation, Step 1 of 1: Read from main memory and compute finite difference stencils:
*/
const double hDD00 = in_gfs[IDX4S(HDD00GF, i0,i1,i2)];
const double hDD01 = in_gfs[IDX4S(HDD01GF, i0,i1,i2)];
const double hDD02 = in_gfs[IDX4S(HDD02GF, i0,i1,i2)];
const double hDD11 = in_gfs[IDX4S(HDD11GF, i0,i1,i2)];
const double hDD12 = in_gfs[IDX4S(HDD12GF, i0,i1,i2)];
const double hDD22 = in_gfs[IDX4S(HDD22GF, i0,i1,i2)];
/*
* NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory:
*/
const double tmp0 = hDD00 + 1;
const double tmp1 = ((sin(xx1))*(sin(xx1)));
const double tmp2 = tmp1*((xx0)*(xx0)*(xx0)*(xx0));
const double tmp3 = ((xx0)*(xx0));
const double tmp4 = hDD11*tmp3 + tmp3;
const double tmp5 = tmp1*tmp3;
const double tmp6 = hDD22*tmp5 + tmp5;
const double tmp7 = cbrt(fabs(tmp2)/(-((hDD01)*(hDD01))*tmp3*tmp6 + 2*hDD01*hDD02*hDD12*tmp2 - ((hDD02)*(hDD02))*tmp4*tmp5 - ((hDD12)*(hDD12))*tmp0*tmp2 + tmp0*tmp4*tmp6));
in_gfs[IDX4S(HDD00GF, i0, i1, i2)] = tmp0*tmp7 - 1;
in_gfs[IDX4S(HDD01GF, i0, i1, i2)] = hDD01*tmp7;
in_gfs[IDX4S(HDD02GF, i0, i1, i2)] = hDD02*tmp7;
in_gfs[IDX4S(HDD11GF, i0, i1, i2)] = tmp7*(hDD11 + 1) - 1;
in_gfs[IDX4S(HDD12GF, i0, i1, i2)] = hDD12*tmp7;
in_gfs[IDX4S(HDD22GF, i0, i1, i2)] = tmp7*(hDD22 + 1) - 1;
} // END LOOP: for(int i0=0; i0<Nxx_plus_2NGHOSTS0; i0++)
} // END LOOP: for(int i1=0; i1<Nxx_plus_2NGHOSTS1; i1++)
} // END LOOP: for(int i2=0; i2<Nxx_plus_2NGHOSTS2; i2++)
}
// Set free-parameter values.
const REAL domain_size = 3.0;
const REAL sinh_width = 0.4;
const REAL sinhv2_const_dr= 0.05;
const REAL SymTP_bScale = 0.5;
params.RMAX = domain_size;
/*
Set up the initial data at all points on the numerical grid.
*/
void initial_data(const paramstruct *restrict params,REAL *restrict xx[3], REAL *restrict in_gfs) {
#include "set_Cparameters.h"
#pragma omp parallel for
for(int i2=0; i2<Nxx_plus_2NGHOSTS2; i2++) {
const REAL xx2 = xx[2][i2];
for(int i1=0; i1<Nxx_plus_2NGHOSTS1; i1++) {
const REAL xx1 = xx[1][i1];
for(int i0=0; i0<Nxx_plus_2NGHOSTS0; i0++) {
const REAL xx0 = xx[0][i0];
{
const double tmp0 = r0 + xx0;
const double tmp1 = M*tmp0;
const double tmp2 = 2*tmp1;
const double tmp3 = 2*r0;
const double tmp4 = 2*xx0;
const double tmp5 = tmp3 + tmp4;
const double tmp6 = tmp0*tmp5;
const double tmp7 = ((a)*(a));
const double tmp8 = 2*xx1;
const double tmp9 = tmp7*cos(tmp8);
const double tmp10 = tmp7 + tmp9;
const double tmp11 = tmp10 + tmp6;
const double tmp12 = 4*tmp1;
const double tmp13 = (1.0/(tmp11 + tmp12));
const double tmp14 = ((tmp0)*(tmp0));
const double tmp15 = cos(xx1);
const double tmp16 = ((tmp15)*(tmp15))*tmp7;
const double tmp17 = tmp14 + tmp16;
const double tmp18 = (1.0/(tmp17));
const double tmp19 = tmp18*tmp2;
const double tmp20 = tmp19 + 1;
const double tmp21 = sqrt(tmp20);
const double tmp22 = M*tmp21;
const double tmp23 = tmp13*tmp22;
const double tmp24 = (1.0/((tmp11)*(tmp11)));
const double tmp25 = tmp24*(tmp10 - tmp6);
const double tmp26 = 4*tmp25;
const double tmp27 = tmp23*tmp26*(tmp11 + tmp2);
const double tmp28 = tmp1*tmp18;
const double tmp29 = sin(xx1);
const double tmp30 = ((tmp29)*(tmp29)*(tmp29)*(tmp29));
const double tmp31 = tmp30*tmp7;
const double tmp32 = ((tmp20)*(tmp20));
const double tmp33 = tmp31*tmp32;
const double tmp34 = ((tmp29)*(tmp29));
const double tmp35 = tmp34*tmp7;
const double tmp36 = tmp14 + tmp19*tmp35 + tmp7;
const double tmp37 = tmp34*tmp36;
const double tmp38 = tmp20*tmp37;
const double tmp39 = -tmp17*tmp33 + tmp17*tmp38;
const double tmp40 = (1.0/(tmp39));
const double tmp41 = tmp17*pow(tmp20, 3.0/2.0)*tmp40;
const double tmp42 = 4*tmp14;
const double tmp43 = tmp23*tmp42;
const double tmp44 = ((a)*(a)*(a)*(a));
const double tmp45 = -M;
const double tmp46 = tmp2*tmp24*(8*((tmp0)*(tmp0)*(tmp0)*(tmp0)*(tmp0)) + 4*tmp0*tmp9*(tmp0*(M + tmp5) + tmp7) + tmp42*tmp7*(tmp45 + tmp5) + tmp44*(tmp0 + tmp45)*cos(4*xx1) + tmp44*(M + 3*r0 + 3*xx0));
const double tmp47 = tmp17*tmp37;
const double tmp48 = -M*tmp26*tmp31*tmp41 + tmp13*tmp34*tmp41*tmp46 + tmp27*tmp40*tmp47 + tmp40*tmp43*(-tmp33 + tmp38);
const double tmp49 = fabs(xx0);
const double tmp50 = pow(tmp49, 4.0/3.0);
const double tmp51 = cbrt(tmp40);
const double tmp52 = fabs(tmp29);
const double tmp53 = tmp51*pow(tmp52, 2.0/3.0);
const double tmp54 = tmp50*tmp53;
const double tmp55 = (1.0/(xx0));
const double tmp56 = tmp54*tmp55;
const double tmp57 = tmp29*tmp7;
const double tmp58 = tmp13*tmp21;
const double tmp59 = 8*tmp15;
const double tmp60 = tmp1*tmp59/tmp11;
const double tmp61 = a*tmp34;
const double tmp62 = 2*tmp61;
const double tmp63 = (1.0/3.0)*tmp48;
const double tmp64 = tmp20*tmp34;
const double tmp65 = a*tmp64;
const double tmp66 = ((xx0)*(xx0));
const double tmp67 = (1.0/(tmp66));
const double tmp68 = tmp54*tmp67;
const double tmp69 = ((a)*(a)*(a));
const double tmp70 = tmp34*tmp58;
const double tmp71 = tmp67/tmp34;
const double tmp72 = tmp40*((xx0)*(xx0)*(xx0)*(xx0));
const double tmp73 = tmp17*tmp32*tmp7;
const double tmp74 = tmp17*tmp20;
const double tmp75 = tmp36*tmp74;
const double tmp76 = -pow(tmp29, 6)*tmp72*tmp73 + tmp30*tmp72*tmp75;
const double tmp77 = (1.0/(tmp76));
const double tmp78 = tmp20*tmp54;
const double tmp79 = a*tmp78;
const double tmp80 = tmp17*tmp54;
const double tmp81 = tmp37*tmp54;
const double tmp82 = (1.0/((tmp76)*(tmp76)));
const double tmp83 = cbrt(tmp49)*tmp53*(((xx0) > 0) - ((xx0) < 0));
const double tmp84 = (4.0/3.0)*tmp83;
const double tmp85 = tmp37*tmp84;
const double tmp86 = M*tmp18;
const double tmp87 = 2*tmp86;
const double tmp88 = ((tmp17)*(tmp17));
const double tmp89 = (1.0/(tmp88));
const double tmp90 = tmp89*(-tmp3 - tmp4);
const double tmp91 = tmp2*tmp90;
const double tmp92 = tmp35*tmp87 + tmp35*tmp91 + tmp5;
const double tmp93 = tmp34*tmp54;
const double tmp94 = tmp92*tmp93;
const double tmp95 = tmp87 + tmp91;
const double tmp96 = tmp17*tmp64;
const double tmp97 = tmp31*tmp74*(tmp12*tmp90 + 4*tmp86) + tmp33*tmp5 - tmp38*tmp5 - tmp47*tmp95 - tmp92*tmp96;
const double tmp98 = (1.0/3.0)*tmp40;
const double tmp99 = tmp97*tmp98;
const double tmp100 = tmp81*tmp99;
const double tmp101 = tmp100 + tmp85 + tmp94;
const double tmp102 = pow(tmp40, 4.0/3.0)*pow(tmp49, 16.0/3.0)*pow(tmp52, 8.0/3.0)*tmp88;
const double tmp103 = -tmp100 - tmp85 - tmp94;
const double tmp104 = pow(tmp40, 2.0/3.0)*pow(tmp49, 8.0/3.0)*pow(tmp52, 4.0/3.0);
const double tmp105 = tmp104*tmp77;
const double tmp106 = tmp105*tmp47;
const double tmp107 = (1.0/2.0)*tmp106;
const double tmp108 = tmp105*tmp74;
const double tmp109 = -tmp17*tmp84 - tmp5*tmp54 - tmp80*tmp99;
const double tmp110 = -tmp104*tmp33 + tmp104*tmp38;
const double tmp111 = tmp110*tmp77;
const double tmp112 = tmp54*tmp95;
const double tmp113 = tmp112 + tmp20*tmp84 + tmp78*tmp99;
const double tmp114 = -tmp112*tmp62 - 2.0/3.0*tmp40*tmp61*tmp78*tmp97 - 8.0/3.0*tmp65*tmp83;
const double tmp115 = a*tmp96;
const double tmp116 = tmp105*tmp115;
const double tmp117 = (1.0/2.0)*tmp116;
const double tmp118 = 2*tmp15;
const double tmp119 = tmp118*tmp29;
const double tmp120 = tmp119*tmp54;
const double tmp121 = (2.0/3.0)*tmp15*tmp50*tmp51*(((tmp29) > 0) - ((tmp29) < 0))/cbrt(tmp52);
const double tmp122 = ((tmp29)*(tmp29)*(tmp29)*(tmp29)*(tmp29))*tmp44;
const double tmp123 = ((tmp29)*(tmp29)*(tmp29));
const double tmp124 = tmp123*tmp15;
const double tmp125 = tmp123*tmp36*tmp7;
const double tmp126 = tmp12*tmp15*tmp18;
const double tmp127 = tmp12*tmp89;
const double tmp128 = tmp124*tmp127;
const double tmp129 = tmp126*tmp57 + tmp128*tmp44;
const double tmp130 = tmp98*(-tmp118*tmp122*tmp32 + tmp118*tmp125*tmp20 - tmp119*tmp75 + tmp122*tmp20*tmp28*tmp59 + 4*tmp124*tmp73 - tmp125*tmp126 - tmp129*tmp96);
const double tmp131 = (1.0/2.0)*tmp82;
const double tmp132 = tmp130*tmp78;
const double tmp133 = tmp104*tmp110;
const double tmp134 = tmp131*tmp133;
const double tmp135 = (1.0/2.0)*tmp108;
in_gfs[IDX4S(ADD00GF,i0,i1,i2)] = tmp54*(tmp27 - tmp48*((2.0/3.0)*tmp28 + 1.0/3.0));
in_gfs[IDX4S(ADD01GF,i0,i1,i2)] = tmp56*tmp57*tmp58*tmp60;
in_gfs[IDX4S(ADD02GF,i0,i1,i2)] = tmp56*(-tmp22*tmp25*tmp62 + tmp63*tmp65)/tmp29;
in_gfs[IDX4S(ADD11GF,i0,i1,i2)] = tmp68*(tmp43 - tmp48*((1.0/3.0)*tmp14 + (1.0/3.0)*tmp16));
in_gfs[IDX4S(ADD12GF,i0,i1,i2)] = -tmp60*tmp68*tmp69*tmp70;
in_gfs[IDX4S(ADD22GF,i0,i1,i2)] = tmp54*tmp71*(-tmp37*tmp63 + tmp46*tmp70);
in_gfs[IDX4S(ALPHAGF,i0,i1,i2)] = (1.0/(tmp21));
in_gfs[IDX4S(BETU0GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(BETU1GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(BETU2GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(CFGF,i0,i1,i2)] = pow(tmp39*tmp77, -1.0/6.0);
in_gfs[IDX4S(HDD00GF,i0,i1,i2)] = tmp78 - 1;
in_gfs[IDX4S(HDD01GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(HDD02GF,i0,i1,i2)] = -tmp29*tmp55*tmp79;
in_gfs[IDX4S(HDD11GF,i0,i1,i2)] = tmp67*(-tmp66 + tmp80);
in_gfs[IDX4S(HDD12GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(HDD22GF,i0,i1,i2)] = tmp71*(-tmp34*tmp66 + tmp81);
in_gfs[IDX4S(LAMBDAU0GF,i0,i1,i2)] = tmp101*tmp102*tmp33*tmp82 + tmp106*(tmp107*tmp113 + tmp114*tmp117) + tmp108*(tmp103*tmp107 + tmp34*xx0) + tmp111*(tmp107*tmp109 + xx0);
in_gfs[IDX4S(LAMBDAU1GF,i0,i1,i2)] = xx0*(tmp108*((1.0/2.0)*tmp111*(-tmp120*tmp36 - tmp121*tmp37 - tmp129*tmp93 - tmp130*tmp81) + (1.0/2.0)*sin(tmp8)) + ((tmp110)*(tmp110))*tmp131*(-tmp120*tmp7 + tmp121*tmp17 + tmp130*tmp80) + tmp115*tmp133*tmp82*(tmp119*tmp79 + tmp121*tmp65 + tmp128*tmp54*tmp69 + tmp132*tmp61) + tmp134*tmp47*(-tmp121*tmp20 - tmp127*tmp15*tmp54*tmp57 - tmp132));
in_gfs[IDX4S(LAMBDAU2GF,i0,i1,i2)] = tmp29*xx0*(tmp102*tmp103*tmp131*tmp32*tmp61 + tmp106*(tmp113*tmp117 + tmp114*tmp135) + tmp109*tmp115*tmp134 + 2*tmp116*(tmp101*tmp135 - tmp55));
in_gfs[IDX4S(TRKGF,i0,i1,i2)] = tmp48;
in_gfs[IDX4S(VETU0GF,i0,i1,i2)] = tmp19/tmp20;
in_gfs[IDX4S(VETU1GF,i0,i1,i2)] = 0;
in_gfs[IDX4S(VETU2GF,i0,i1,i2)] = 0;
}
} // END LOOP: for(int i0=0; i0<Nxx_plus_2NGHOSTS0; i0++)
} // END LOOP: for(int i1=0; i1<Nxx_plus_2NGHOSTS1; i1++)
} // END LOOP: for(int i2=0; i2<Nxx_plus_2NGHOSTS2; i2++)
}
/*
Evaluate the momentum constraint
*/
void momentum_constraint(const paramstruct *restrict params, REAL *restrict xx[3],
REAL *restrict in_gfs, REAL *restrict aux_gfs) {
#include "set_Cparameters.h"
#pragma omp parallel for
for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) {
const REAL xx2 = xx[2][i2];
for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) {
const REAL xx1 = xx[1][i1];
for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) {
const REAL xx0 = xx[0][i0];
{
/*
* NRPy+ Finite Difference Code Generation, Step 1 of 2: Read from main memory and compute finite difference stencils:
*/
const double hDD00_i0_i1m2_i2 = in_gfs[IDX4S(HDD00GF, i0,i1-2,i2)];
const double hDD00_i0_i1m1_i2 = in_gfs[IDX4S(HDD00GF, i0,i1-1,i2)];
const double hDD00_i0m2_i1_i2 = in_gfs[IDX4S(HDD00GF, i0-2,i1,i2)];
const double hDD00_i0m1_i1_i2 = in_gfs[IDX4S(HDD00GF, i0-1,i1,i2)];
const double hDD00 = in_gfs[IDX4S(HDD00GF, i0,i1,i2)];
const double hDD00_i0p1_i1_i2 = in_gfs[IDX4S(HDD00GF, i0+1,i1,i2)];
const double hDD00_i0p2_i1_i2 = in_gfs[IDX4S(HDD00GF, i0+2,i1,i2)];
const double hDD00_i0_i1p1_i2 = in_gfs[IDX4S(HDD00GF, i0,i1+1,i2)];
const double hDD00_i0_i1p2_i2 = in_gfs[IDX4S(HDD00GF, i0,i1+2,i2)];
const double hDD01_i0_i1m2_i2 = in_gfs[IDX4S(HDD01GF, i0,i1-2,i2)];
const double hDD01_i0_i1m1_i2 = in_gfs[IDX4S(HDD01GF, i0,i1-1,i2)];
const double hDD01_i0m2_i1_i2 = in_gfs[IDX4S(HDD01GF, i0-2,i1,i2)];
const double hDD01_i0m1_i1_i2 = in_gfs[IDX4S(HDD01GF, i0-1,i1,i2)];
const double hDD01 = in_gfs[IDX4S(HDD01GF, i0,i1,i2)];
const double hDD01_i0p1_i1_i2 = in_gfs[IDX4S(HDD01GF, i0+1,i1,i2)];
const double hDD01_i0p2_i1_i2 = in_gfs[IDX4S(HDD01GF, i0+2,i1,i2)];
const double hDD01_i0_i1p1_i2 = in_gfs[IDX4S(HDD01GF, i0,i1+1,i2)];
const double hDD01_i0_i1p2_i2 = in_gfs[IDX4S(HDD01GF, i0,i1+2,i2)];
const double hDD02_i0_i1m2_i2 = in_gfs[IDX4S(HDD02GF, i0,i1-2,i2)];
const double hDD02_i0_i1m1_i2 = in_gfs[IDX4S(HDD02GF, i0,i1-1,i2)];
const double hDD02_i0m2_i1_i2 = in_gfs[IDX4S(HDD02GF, i0-2,i1,i2)];
const double hDD02_i0m1_i1_i2 = in_gfs[IDX4S(HDD02GF, i0-1,i1,i2)];
const double hDD02 = in_gfs[IDX4S(HDD02GF, i0,i1,i2)];
const double hDD02_i0p1_i1_i2 = in_gfs[IDX4S(HDD02GF, i0+1,i1,i2)];
const double hDD02_i0p2_i1_i2 = in_gfs[IDX4S(HDD02GF, i0+2,i1,i2)];
const double hDD02_i0_i1p1_i2 = in_gfs[IDX4S(HDD02GF, i0,i1+1,i2)];
const double hDD02_i0_i1p2_i2 = in_gfs[IDX4S(HDD02GF, i0,i1+2,i2)];
const double hDD11_i0_i1m2_i2 = in_gfs[IDX4S(HDD11GF, i0,i1-2,i2)];
const double hDD11_i0_i1m1_i2 = in_gfs[IDX4S(HDD11GF, i0,i1-1,i2)];
const double hDD11_i0m2_i1_i2 = in_gfs[IDX4S(HDD11GF, i0-2,i1,i2)];
const double hDD11_i0m1_i1_i2 = in_gfs[IDX4S(HDD11GF, i0-1,i1,i2)];
const double hDD11 = in_gfs[IDX4S(HDD11GF, i0,i1,i2)];
const double hDD11_i0p1_i1_i2 = in_gfs[IDX4S(HDD11GF, i0+1,i1,i2)];
const double hDD11_i0p2_i1_i2 = in_gfs[IDX4S(HDD11GF, i0+2,i1,i2)];
const double hDD11_i0_i1p1_i2 = in_gfs[IDX4S(HDD11GF, i0,i1+1,i2)];
const double hDD11_i0_i1p2_i2 = in_gfs[IDX4S(HDD11GF, i0,i1+2,i2)];
const double hDD12_i0_i1m2_i2 = in_gfs[IDX4S(HDD12GF, i0,i1-2,i2)];
const double hDD12_i0_i1m1_i2 = in_gfs[IDX4S(HDD12GF, i0,i1-1,i2)];
const double hDD12_i0m2_i1_i2 = in_gfs[IDX4S(HDD12GF, i0-2,i1,i2)];
const double hDD12_i0m1_i1_i2 = in_gfs[IDX4S(HDD12GF, i0-1,i1,i2)];
const double hDD12 = in_gfs[IDX4S(HDD12GF, i0,i1,i2)];
const double hDD12_i0p1_i1_i2 = in_gfs[IDX4S(HDD12GF, i0+1,i1,i2)];
const double hDD12_i0p2_i1_i2 = in_gfs[IDX4S(HDD12GF, i0+2,i1,i2)];
const double hDD12_i0_i1p1_i2 = in_gfs[IDX4S(HDD12GF, i0,i1+1,i2)];
const double hDD12_i0_i1p2_i2 = in_gfs[IDX4S(HDD12GF, i0,i1+2,i2)];
const double hDD22_i0_i1m2_i2 = in_gfs[IDX4S(HDD22GF, i0,i1-2,i2)];
const double hDD22_i0_i1m1_i2 = in_gfs[IDX4S(HDD22GF, i0,i1-1,i2)];
const double hDD22_i0m2_i1_i2 = in_gfs[IDX4S(HDD22GF, i0-2,i1,i2)];
const double hDD22_i0m1_i1_i2 = in_gfs[IDX4S(HDD22GF, i0-1,i1,i2)];
const double hDD22 = in_gfs[IDX4S(HDD22GF, i0,i1,i2)];
const double hDD22_i0p1_i1_i2 = in_gfs[IDX4S(HDD22GF, i0+1,i1,i2)];
const double hDD22_i0p2_i1_i2 = in_gfs[IDX4S(HDD22GF, i0+2,i1,i2)];
const double hDD22_i0_i1p1_i2 = in_gfs[IDX4S(HDD22GF, i0,i1+1,i2)];
const double hDD22_i0_i1p2_i2 = in_gfs[IDX4S(HDD22GF, i0,i1+2,i2)];
const double aDD00_i0_i1m2_i2 = in_gfs[IDX4S(ADD00GF, i0,i1-2,i2)];
const double aDD00_i0_i1m1_i2 = in_gfs[IDX4S(ADD00GF, i0,i1-1,i2)];
const double aDD00_i0m2_i1_i2 = in_gfs[IDX4S(ADD00GF, i0-2,i1,i2)];
const double aDD00_i0m1_i1_i2 = in_gfs[IDX4S(ADD00GF, i0-1,i1,i2)];
const double aDD00 = in_gfs[IDX4S(ADD00GF, i0,i1,i2)];
const double aDD00_i0p1_i1_i2 = in_gfs[IDX4S(ADD00GF, i0+1,i1,i2)];
const double aDD00_i0p2_i1_i2 = in_gfs[IDX4S(ADD00GF, i0+2,i1,i2)];
const double aDD00_i0_i1p1_i2 = in_gfs[IDX4S(ADD00GF, i0,i1+1,i2)];
const double aDD00_i0_i1p2_i2 = in_gfs[IDX4S(ADD00GF, i0,i1+2,i2)];
const double aDD01_i0_i1m2_i2 = in_gfs[IDX4S(ADD01GF, i0,i1-2,i2)];
const double aDD01_i0_i1m1_i2 = in_gfs[IDX4S(ADD01GF, i0,i1-1,i2)];
const double aDD01_i0m2_i1_i2 = in_gfs[IDX4S(ADD01GF, i0-2,i1,i2)];
const double aDD01_i0m1_i1_i2 = in_gfs[IDX4S(ADD01GF, i0-1,i1,i2)];
const double aDD01 = in_gfs[IDX4S(ADD01GF, i0,i1,i2)];
const double aDD01_i0p1_i1_i2 = in_gfs[IDX4S(ADD01GF, i0+1,i1,i2)];
const double aDD01_i0p2_i1_i2 = in_gfs[IDX4S(ADD01GF, i0+2,i1,i2)];
const double aDD01_i0_i1p1_i2 = in_gfs[IDX4S(ADD01GF, i0,i1+1,i2)];
const double aDD01_i0_i1p2_i2 = in_gfs[IDX4S(ADD01GF, i0,i1+2,i2)];
const double aDD02_i0_i1m2_i2 = in_gfs[IDX4S(ADD02GF, i0,i1-2,i2)];
const double aDD02_i0_i1m1_i2 = in_gfs[IDX4S(ADD02GF, i0,i1-1,i2)];
const double aDD02_i0m2_i1_i2 = in_gfs[IDX4S(ADD02GF, i0-2,i1,i2)];
const double aDD02_i0m1_i1_i2 = in_gfs[IDX4S(ADD02GF, i0-1,i1,i2)];
const double aDD02 = in_gfs[IDX4S(ADD02GF, i0,i1,i2)];
const double aDD02_i0p1_i1_i2 = in_gfs[IDX4S(ADD02GF, i0+1,i1,i2)];
const double aDD02_i0p2_i1_i2 = in_gfs[IDX4S(ADD02GF, i0+2,i1,i2)];
const double aDD02_i0_i1p1_i2 = in_gfs[IDX4S(ADD02GF, i0,i1+1,i2)];
const double aDD02_i0_i1p2_i2 = in_gfs[IDX4S(ADD02GF, i0,i1+2,i2)];
const double aDD11_i0_i1m2_i2 = in_gfs[IDX4S(ADD11GF, i0,i1-2,i2)];
const double aDD11_i0_i1m1_i2 = in_gfs[IDX4S(ADD11GF, i0,i1-1,i2)];
const double aDD11_i0m2_i1_i2 = in_gfs[IDX4S(ADD11GF, i0-2,i1,i2)];
const double aDD11_i0m1_i1_i2 = in_gfs[IDX4S(ADD11GF, i0-1,i1,i2)];
const double aDD11 = in_gfs[IDX4S(ADD11GF, i0,i1,i2)];
const double aDD11_i0p1_i1_i2 = in_gfs[IDX4S(ADD11GF, i0+1,i1,i2)];
const double aDD11_i0p2_i1_i2 = in_gfs[IDX4S(ADD11GF, i0+2,i1,i2)];
const double aDD11_i0_i1p1_i2 = in_gfs[IDX4S(ADD11GF, i0,i1+1,i2)];
const double aDD11_i0_i1p2_i2 = in_gfs[IDX4S(ADD11GF, i0,i1+2,i2)];
const double aDD12_i0_i1m2_i2 = in_gfs[IDX4S(ADD12GF, i0,i1-2,i2)];
const double aDD12_i0_i1m1_i2 = in_gfs[IDX4S(ADD12GF, i0,i1-1,i2)];
const double aDD12_i0m2_i1_i2 = in_gfs[IDX4S(ADD12GF, i0-2,i1,i2)];
const double aDD12_i0m1_i1_i2 = in_gfs[IDX4S(ADD12GF, i0-1,i1,i2)];
const double aDD12 = in_gfs[IDX4S(ADD12GF, i0,i1,i2)];
const double aDD12_i0p1_i1_i2 = in_gfs[IDX4S(ADD12GF, i0+1,i1,i2)];
const double aDD12_i0p2_i1_i2 = in_gfs[IDX4S(ADD12GF, i0+2,i1,i2)];
const double aDD12_i0_i1p1_i2 = in_gfs[IDX4S(ADD12GF, i0,i1+1,i2)];
const double aDD12_i0_i1p2_i2 = in_gfs[IDX4S(ADD12GF, i0,i1+2,i2)];
const double aDD22_i0_i1m2_i2 = in_gfs[IDX4S(ADD22GF, i0,i1-2,i2)];
const double aDD22_i0_i1m1_i2 = in_gfs[IDX4S(ADD22GF, i0,i1-1,i2)];
const double aDD22_i0m2_i1_i2 = in_gfs[IDX4S(ADD22GF, i0-2,i1,i2)];
const double aDD22_i0m1_i1_i2 = in_gfs[IDX4S(ADD22GF, i0-1,i1,i2)];
const double aDD22 = in_gfs[IDX4S(ADD22GF, i0,i1,i2)];
const double aDD22_i0p1_i1_i2 = in_gfs[IDX4S(ADD22GF, i0+1,i1,i2)];
const double aDD22_i0p2_i1_i2 = in_gfs[IDX4S(ADD22GF, i0+2,i1,i2)];
const double aDD22_i0_i1p1_i2 = in_gfs[IDX4S(ADD22GF, i0,i1+1,i2)];
const double aDD22_i0_i1p2_i2 = in_gfs[IDX4S(ADD22GF, i0,i1+2,i2)];
const double trK_i0_i1m2_i2 = in_gfs[IDX4S(TRKGF, i0,i1-2,i2)];
const double trK_i0_i1m1_i2 = in_gfs[IDX4S(TRKGF, i0,i1-1,i2)];
const double trK_i0m2_i1_i2 = in_gfs[IDX4S(TRKGF, i0-2,i1,i2)];
const double trK_i0m1_i1_i2 = in_gfs[IDX4S(TRKGF, i0-1,i1,i2)];
const double trK_i0p1_i1_i2 = in_gfs[IDX4S(TRKGF, i0+1,i1,i2)];
const double trK_i0p2_i1_i2 = in_gfs[IDX4S(TRKGF, i0+2,i1,i2)];
const double trK_i0_i1p1_i2 = in_gfs[IDX4S(TRKGF, i0,i1+1,i2)];
const double trK_i0_i1p2_i2 = in_gfs[IDX4S(TRKGF, i0,i1+2,i2)];
const double cf_i0_i1m2_i2 = in_gfs[IDX4S(CFGF, i0,i1-2,i2)];
const double cf_i0_i1m1_i2 = in_gfs[IDX4S(CFGF, i0,i1-1,i2)];
const double cf_i0m2_i1_i2 = in_gfs[IDX4S(CFGF, i0-2,i1,i2)];
const double cf_i0m1_i1_i2 = in_gfs[IDX4S(CFGF, i0-1,i1,i2)];
const double cf = in_gfs[IDX4S(CFGF, i0,i1,i2)];
const double cf_i0p1_i1_i2 = in_gfs[IDX4S(CFGF, i0+1,i1,i2)];
const double cf_i0p2_i1_i2 = in_gfs[IDX4S(CFGF, i0+2,i1,i2)];
const double cf_i0_i1p1_i2 = in_gfs[IDX4S(CFGF, i0,i1+1,i2)];
const double cf_i0_i1p2_i2 = in_gfs[IDX4S(CFGF, i0,i1+2,i2)];
const double aDD_dD000 = invdx0*(-2.0/3.0*aDD00_i0m1_i1_i2 + (1.0/12.0)*aDD00_i0m2_i1_i2 + (2.0/3.0)*aDD00_i0p1_i1_i2 - 1.0/12.0*aDD00_i0p2_i1_i2);
const double aDD_dD001 = invdx1*(-2.0/3.0*aDD00_i0_i1m1_i2 + (1.0/12.0)*aDD00_i0_i1m2_i2 + (2.0/3.0)*aDD00_i0_i1p1_i2 - 1.0/12.0*aDD00_i0_i1p2_i2);
const double aDD_dD010 = invdx0*(-2.0/3.0*aDD01_i0m1_i1_i2 + (1.0/12.0)*aDD01_i0m2_i1_i2 + (2.0/3.0)*aDD01_i0p1_i1_i2 - 1.0/12.0*aDD01_i0p2_i1_i2);
const double aDD_dD011 = invdx1*(-2.0/3.0*aDD01_i0_i1m1_i2 + (1.0/12.0)*aDD01_i0_i1m2_i2 + (2.0/3.0)*aDD01_i0_i1p1_i2 - 1.0/12.0*aDD01_i0_i1p2_i2);
const double aDD_dD020 = invdx0*(-2.0/3.0*aDD02_i0m1_i1_i2 + (1.0/12.0)*aDD02_i0m2_i1_i2 + (2.0/3.0)*aDD02_i0p1_i1_i2 - 1.0/12.0*aDD02_i0p2_i1_i2);
const double aDD_dD021 = invdx1*(-2.0/3.0*aDD02_i0_i1m1_i2 + (1.0/12.0)*aDD02_i0_i1m2_i2 + (2.0/3.0)*aDD02_i0_i1p1_i2 - 1.0/12.0*aDD02_i0_i1p2_i2);
const double aDD_dD110 = invdx0*(-2.0/3.0*aDD11_i0m1_i1_i2 + (1.0/12.0)*aDD11_i0m2_i1_i2 + (2.0/3.0)*aDD11_i0p1_i1_i2 - 1.0/12.0*aDD11_i0p2_i1_i2);
const double aDD_dD111 = invdx1*(-2.0/3.0*aDD11_i0_i1m1_i2 + (1.0/12.0)*aDD11_i0_i1m2_i2 + (2.0/3.0)*aDD11_i0_i1p1_i2 - 1.0/12.0*aDD11_i0_i1p2_i2);
const double aDD_dD120 = invdx0*(-2.0/3.0*aDD12_i0m1_i1_i2 + (1.0/12.0)*aDD12_i0m2_i1_i2 + (2.0/3.0)*aDD12_i0p1_i1_i2 - 1.0/12.0*aDD12_i0p2_i1_i2);
const double aDD_dD121 = invdx1*(-2.0/3.0*aDD12_i0_i1m1_i2 + (1.0/12.0)*aDD12_i0_i1m2_i2 + (2.0/3.0)*aDD12_i0_i1p1_i2 - 1.0/12.0*aDD12_i0_i1p2_i2);
const double aDD_dD220 = invdx0*(-2.0/3.0*aDD22_i0m1_i1_i2 + (1.0/12.0)*aDD22_i0m2_i1_i2 + (2.0/3.0)*aDD22_i0p1_i1_i2 - 1.0/12.0*aDD22_i0p2_i1_i2);
const double aDD_dD221 = invdx1*(-2.0/3.0*aDD22_i0_i1m1_i2 + (1.0/12.0)*aDD22_i0_i1m2_i2 + (2.0/3.0)*aDD22_i0_i1p1_i2 - 1.0/12.0*aDD22_i0_i1p2_i2);
const double cf_dD0 = invdx0*(-2.0/3.0*cf_i0m1_i1_i2 + (1.0/12.0)*cf_i0m2_i1_i2 + (2.0/3.0)*cf_i0p1_i1_i2 - 1.0/12.0*cf_i0p2_i1_i2);
const double cf_dD1 = invdx1*(-2.0/3.0*cf_i0_i1m1_i2 + (1.0/12.0)*cf_i0_i1m2_i2 + (2.0/3.0)*cf_i0_i1p1_i2 - 1.0/12.0*cf_i0_i1p2_i2);
const double hDD_dD000 = invdx0*(-2.0/3.0*hDD00_i0m1_i1_i2 + (1.0/12.0)*hDD00_i0m2_i1_i2 + (2.0/3.0)*hDD00_i0p1_i1_i2 - 1.0/12.0*hDD00_i0p2_i1_i2);
const double hDD_dD001 = invdx1*(-2.0/3.0*hDD00_i0_i1m1_i2 + (1.0/12.0)*hDD00_i0_i1m2_i2 + (2.0/3.0)*hDD00_i0_i1p1_i2 - 1.0/12.0*hDD00_i0_i1p2_i2);
const double hDD_dD010 = invdx0*(-2.0/3.0*hDD01_i0m1_i1_i2 + (1.0/12.0)*hDD01_i0m2_i1_i2 + (2.0/3.0)*hDD01_i0p1_i1_i2 - 1.0/12.0*hDD01_i0p2_i1_i2);
const double hDD_dD011 = invdx1*(-2.0/3.0*hDD01_i0_i1m1_i2 + (1.0/12.0)*hDD01_i0_i1m2_i2 + (2.0/3.0)*hDD01_i0_i1p1_i2 - 1.0/12.0*hDD01_i0_i1p2_i2);
const double hDD_dD020 = invdx0*(-2.0/3.0*hDD02_i0m1_i1_i2 + (1.0/12.0)*hDD02_i0m2_i1_i2 + (2.0/3.0)*hDD02_i0p1_i1_i2 - 1.0/12.0*hDD02_i0p2_i1_i2);
const double hDD_dD021 = invdx1*(-2.0/3.0*hDD02_i0_i1m1_i2 + (1.0/12.0)*hDD02_i0_i1m2_i2 + (2.0/3.0)*hDD02_i0_i1p1_i2 - 1.0/12.0*hDD02_i0_i1p2_i2);
const double hDD_dD110 = invdx0*(-2.0/3.0*hDD11_i0m1_i1_i2 + (1.0/12.0)*hDD11_i0m2_i1_i2 + (2.0/3.0)*hDD11_i0p1_i1_i2 - 1.0/12.0*hDD11_i0p2_i1_i2);
const double hDD_dD111 = invdx1*(-2.0/3.0*hDD11_i0_i1m1_i2 + (1.0/12.0)*hDD11_i0_i1m2_i2 + (2.0/3.0)*hDD11_i0_i1p1_i2 - 1.0/12.0*hDD11_i0_i1p2_i2);
const double hDD_dD120 = invdx0*(-2.0/3.0*hDD12_i0m1_i1_i2 + (1.0/12.0)*hDD12_i0m2_i1_i2 + (2.0/3.0)*hDD12_i0p1_i1_i2 - 1.0/12.0*hDD12_i0p2_i1_i2);
const double hDD_dD121 = invdx1*(-2.0/3.0*hDD12_i0_i1m1_i2 + (1.0/12.0)*hDD12_i0_i1m2_i2 + (2.0/3.0)*hDD12_i0_i1p1_i2 - 1.0/12.0*hDD12_i0_i1p2_i2);
const double hDD_dD220 = invdx0*(-2.0/3.0*hDD22_i0m1_i1_i2 + (1.0/12.0)*hDD22_i0m2_i1_i2 + (2.0/3.0)*hDD22_i0p1_i1_i2 - 1.0/12.0*hDD22_i0p2_i1_i2);
const double hDD_dD221 = invdx1*(-2.0/3.0*hDD22_i0_i1m1_i2 + (1.0/12.0)*hDD22_i0_i1m2_i2 + (2.0/3.0)*hDD22_i0_i1p1_i2 - 1.0/12.0*hDD22_i0_i1p2_i2);
const double trK_dD0 = invdx0*(-2.0/3.0*trK_i0m1_i1_i2 + (1.0/12.0)*trK_i0m2_i1_i2 + (2.0/3.0)*trK_i0p1_i1_i2 - 1.0/12.0*trK_i0p2_i1_i2);
const double trK_dD1 = invdx1*(-2.0/3.0*trK_i0_i1m1_i2 + (1.0/12.0)*trK_i0_i1m2_i2 + (2.0/3.0)*trK_i0_i1p1_i2 - 1.0/12.0*trK_i0_i1p2_i2);
/*
* NRPy+ Finite Difference Code Generation, Step 2 of 2: Evaluate SymPy expressions and write to main memory:
*/
const double tmp0 = ((cf)*(cf));
const double tmp1 = sin(xx1);
const double tmp2 = ((tmp1)*(tmp1));
const double tmp3 = hDD12*((xx0)*(xx0)*(xx0));
const double tmp4 = ((xx0)*(xx0));
const double tmp5 = tmp2*tmp4;
const double tmp6 = hDD22*tmp5 + tmp5;
const double tmp7 = -hDD01*tmp6*xx0 + hDD02*tmp2*tmp3;
const double tmp8 = 2*hDD01;
const double tmp9 = tmp2*((xx0)*(xx0)*(xx0)*(xx0));
const double tmp10 = hDD00 + 1;
const double tmp11 = ((hDD12)*(hDD12))*tmp9;
const double tmp12 = hDD11*tmp4 + tmp4;
const double tmp13 = ((hDD02)*(hDD02))*tmp5;
const double tmp14 = ((hDD01)*(hDD01))*tmp4;
const double tmp15 = tmp12*tmp6;
const double tmp16 = hDD02*hDD12*tmp8*tmp9 - tmp10*tmp11 + tmp10*tmp15 - tmp12*tmp13 - tmp14*tmp6;
const double tmp17 = (1.0/(tmp16));
const double tmp18 = (2.0/3.0)*tmp17;
const double tmp19 = tmp18*trK_dD1;
const double tmp20 = -tmp11 + tmp15;
const double tmp21 = tmp18*trK_dD0;
const double tmp22 = (1.0/((tmp16)*(tmp16)));
const double tmp23 = hDD02*tmp1;
const double tmp24 = hDD01*tmp1*tmp3 - tmp12*tmp23*xx0;
const double tmp25 = tmp22*((tmp24)*(tmp24));
const double tmp26 = aDD22*tmp5;
const double tmp27 = 6*tmp26;
const double tmp28 = aDD11*tmp4;
const double tmp29 = tmp22*((tmp7)*(tmp7));
const double tmp30 = 6*tmp29;
const double tmp31 = ((tmp20)*(tmp20))*tmp22;
const double tmp32 = 6*aDD00;
const double tmp33 = tmp22*tmp7;
const double tmp34 = tmp24*tmp33;
const double tmp35 = tmp1*tmp4;
const double tmp36 = aDD12*tmp35;
const double tmp37 = 12*tmp36;
const double tmp38 = tmp20*tmp22;
const double tmp39 = tmp24*tmp38;
const double tmp40 = aDD02*tmp1;
const double tmp41 = tmp40*xx0;
const double tmp42 = 12*tmp41;
const double tmp43 = tmp38*tmp7;
const double tmp44 = aDD01*xx0;
const double tmp45 = 12*tmp44;
const double tmp46 = (1.0/2.0)/cf;
const double tmp47 = cf_dD0*tmp46;
const double tmp48 = 2*xx0;
const double tmp49 = tmp2*tmp48;
const double tmp50 = hDD01*tmp23*tmp4 - hDD12*tmp10*tmp35;
const double tmp51 = cos(xx1);
const double tmp52 = tmp51*xx0;
const double tmp53 = hDD02*tmp52;
const double tmp54 = tmp1*xx0;
const double tmp55 = hDD_dD021*tmp54;
const double tmp56 = hDD_dD120*tmp35;
const double tmp57 = tmp1*tmp48;
const double tmp58 = hDD12*tmp57;
const double tmp59 = tmp56 + tmp58;
const double tmp60 = (1.0/2.0)*tmp17;
const double tmp61 = tmp60*(-tmp53 - tmp55 + tmp59);
const double tmp62 = tmp10*tmp12 - tmp14;
const double tmp63 = hDD22*tmp49;
const double tmp64 = hDD_dD220*tmp5;
const double tmp65 = tmp60*(tmp49 + tmp63 + tmp64);
const double tmp66 = -tmp50*tmp61 - tmp62*tmp65;
const double tmp67 = tmp26*tmp66;
const double tmp68 = -tmp24*tmp65 - tmp61*tmp7;
const double tmp69 = tmp40*tmp48;
const double tmp70 = tmp10*tmp6 - tmp13;
const double tmp71 = -tmp50*tmp65 - tmp61*tmp70;
const double tmp72 = 2*tmp35;
const double tmp73 = aDD12*tmp72;
const double tmp74 = aDD22*tmp49 + aDD_dD220*tmp5 + 2*tmp67 + tmp68*tmp69 + tmp71*tmp73;
const double tmp75 = aDD01*tmp48;
const double tmp76 = aDD00*tmp68;
const double tmp77 = tmp66*tmp69 + tmp71*tmp75 + 2*tmp76;
const double tmp78 = tmp24*tmp77;
const double tmp79 = tmp53 + tmp55;
const double tmp80 = tmp60*(-tmp56 - tmp58 + tmp79);
const double tmp81 = tmp4*sin(2*xx1);
const double tmp82 = hDD_dD221*tmp5;
const double tmp83 = tmp51*tmp72;
const double tmp84 = hDD22*tmp83;
const double tmp85 = tmp60*(tmp81 + tmp82 + tmp84);
const double tmp86 = -tmp24*tmp80 - tmp62*tmp85;
const double tmp87 = -tmp50*tmp85 - tmp7*tmp80;
const double tmp88 = tmp28*tmp87;
const double tmp89 = -tmp20*tmp80 - tmp24*tmp85;
const double tmp90 = tmp73*tmp86 + tmp75*tmp89 + 2*tmp88;
const double tmp91 = tmp50*tmp90;
const double tmp92 = tmp26*tmp86;
const double tmp93 = aDD22*tmp83 + aDD_dD221*tmp5 + tmp69*tmp89 + tmp73*tmp87 + 2*tmp92;
const double tmp94 = tmp50*tmp93;
const double tmp95 = tmp22*tmp24;
const double tmp96 = tmp60*(-tmp81 - tmp82 - tmp84);
const double tmp97 = tmp60*(-tmp49 - tmp63 - tmp64);
const double tmp98 = -tmp24*tmp97 - tmp50*tmp96;
const double tmp99 = -tmp7*tmp97 - tmp70*tmp96;
const double tmp100 = -tmp20*tmp97 - tmp7*tmp96;
const double tmp101 = tmp22*(tmp100*tmp69 + 2*tmp26*tmp98 + tmp73*tmp99);
const double tmp102 = tmp101*tmp62;
const double tmp103 = hDD_dD000*tmp60;
const double tmp104 = tmp60*(-hDD_dD001 + hDD_dD010*tmp48 + tmp8);
const double tmp105 = tmp60*(hDD_dD020*tmp57 + 2*tmp23);
const double tmp106 = -tmp103*tmp24 - tmp104*tmp50 - tmp105*tmp62;
const double tmp107 = -tmp103*tmp7 - tmp104*tmp70 - tmp105*tmp50;
const double tmp108 = -tmp103*tmp20 - tmp104*tmp7 - tmp105*tmp24;
const double tmp109 = 2*aDD00*tmp108 + aDD_dD000 + tmp106*tmp69 + tmp107*tmp75;
const double tmp110 = hDD_dD001*tmp60;
const double tmp111 = hDD11*tmp48;
const double tmp112 = hDD_dD110*tmp4;
const double tmp113 = tmp60*(tmp111 + tmp112 + tmp48);
const double tmp114 = tmp60*(tmp59 + tmp79);
const double tmp115 = -tmp110*tmp24 - tmp113*tmp50 - tmp114*tmp62;
const double tmp116 = -tmp110*tmp7 - tmp113*tmp70 - tmp114*tmp50;
const double tmp117 = tmp116*tmp28;
const double tmp118 = -tmp110*tmp20 - tmp113*tmp7 - tmp114*tmp24;
const double tmp119 = aDD11*tmp48 + aDD_dD110*tmp4 + tmp115*tmp73 + 2*tmp117 + tmp118*tmp75;
const double tmp120 = aDD00*tmp118;
const double tmp121 = aDD_dD001 + tmp115*tmp69 + tmp116*tmp75 + 2*tmp120;
const double tmp122 = tmp50*tmp95;
const double tmp123 = tmp33*tmp50;
const double tmp124 = 6*tmp36;
const double tmp125 = 6*tmp41;
const double tmp126 = tmp38*tmp50;
const double tmp127 = tmp22*tmp70;
const double tmp128 = tmp127*tmp24;
const double tmp129 = tmp127*tmp7;
const double tmp130 = 6*tmp28;
const double tmp131 = tmp127*tmp20;
const double tmp132 = 6*tmp44;
const double tmp133 = tmp122*tmp27 + tmp123*tmp124 + tmp124*tmp128 + tmp125*tmp126 + tmp125*tmp34 + tmp129*tmp130 + tmp131*tmp132 + tmp30*tmp44 + tmp32*tmp43;
const double tmp134 = cf_dD1*tmp46;
const double tmp135 = hDD_dD111*tmp4*tmp60;
const double tmp136 = tmp4*tmp51;
const double tmp137 = tmp60*(2*hDD12*tmp136 + hDD_dD121*tmp72);
const double tmp138 = tmp60*(hDD_dD011*tmp48 - tmp111 - tmp112 - tmp48);
const double tmp139 = -tmp135*tmp50 - tmp137*tmp62 - tmp138*tmp24;
const double tmp140 = -tmp135*tmp70 - tmp137*tmp50 - tmp138*tmp7;
const double tmp141 = -tmp135*tmp7 - tmp137*tmp24 - tmp138*tmp20;
const double tmp142 = tmp22*(aDD_dD111*tmp4 + tmp139*tmp73 + 2*tmp140*tmp28 + tmp141*tmp75);
const double tmp143 = tmp142*tmp70;
const double tmp144 = aDD00*tmp89 + tmp41*tmp86 + tmp44*tmp87;
const double tmp145 = tmp28*tmp71 + tmp36*tmp66 + tmp44*tmp68;
const double tmp146 = tmp144 + tmp145;
const double tmp147 = aDD00*tmp100 + tmp36*tmp71 + tmp41*tmp68 + tmp41*tmp98 + tmp44*tmp99 + tmp67;
const double tmp148 = tmp22*tmp62;
const double tmp149 = tmp147*tmp148;
const double tmp150 = tmp100*tmp44 + tmp28*tmp99 + tmp36*tmp87 + tmp36*tmp98 + tmp41*tmp89 + tmp92;
const double tmp151 = tmp148*tmp150;
const double tmp152 = aDD_dD020*tmp54 + tmp106*tmp26 + tmp107*tmp36 + tmp108*tmp41 + tmp40 + tmp41*tmp66 + tmp44*tmp71 + tmp76;
const double tmp153 = tmp115*tmp26 + tmp116*tmp36 + tmp118*tmp41;
const double tmp154 = aDD12*tmp57 + aDD_dD120*tmp35 + tmp145 + tmp153;
const double tmp155 = aDD02*tmp52 + aDD_dD021*tmp54 + tmp144 + tmp153;
const double tmp156 = aDD12*tmp136 + aDD_dD121*tmp35 + tmp139*tmp26 + tmp140*tmp36 + tmp141*tmp41 + tmp36*tmp86 + tmp44*tmp89 + tmp88;
const double tmp157 = aDD01 + aDD_dD010*xx0 + tmp106*tmp36 + tmp107*tmp28 + tmp108*tmp44 + tmp115*tmp41 + tmp116*tmp44 + tmp120;
const double tmp158 = aDD00*tmp141 + aDD_dD011*xx0 + tmp115*tmp36 + tmp117 + tmp118*tmp44 + tmp139*tmp41 + tmp140*tmp44;
const double tmp159 = tmp22*((tmp50)*(tmp50));
const double tmp160 = ((tmp70)*(tmp70));
const double tmp161 = tmp127*tmp50;
const double tmp162 = 2*tmp50;
const double tmp163 = tmp148*tmp24;
const double tmp164 = tmp148*tmp7;
const double tmp165 = tmp148*tmp70;
const double tmp166 = tmp148*tmp20;
aux_gfs[IDX4S(MU0GF, i0, i1, i2)] = tmp0*(tmp102*tmp24 + tmp109*tmp31 + tmp119*tmp29 + tmp121*tmp43 + tmp122*tmp150 + tmp123*tmp156 + tmp126*tmp146 + tmp126*tmp155 + tmp128*tmp156 + tmp131*tmp158 - tmp133*tmp134 + tmp143*tmp7 + tmp146*tmp34 + tmp147*tmp25 + tmp149*tmp20 + tmp151*tmp7 + 2*tmp152*tmp39 + 2*tmp154*tmp34 + tmp155*tmp34 + 2*tmp157*tmp43 + tmp158*tmp29 - tmp19*tmp7 - tmp20*tmp21 + tmp25*tmp74 + tmp33*tmp91 + tmp38*tmp78 - tmp47*(tmp25*tmp27 + tmp28*tmp30 + tmp31*tmp32 + tmp34*tmp37 + tmp39*tmp42 + tmp43*tmp45) + tmp94*tmp95);
aux_gfs[IDX4S(MU1GF, i0, i1, i2)] = tmp0*xx0*(tmp102*tmp50 + tmp109*tmp43 + tmp119*tmp129 + tmp121*tmp29 + tmp122*tmp147 + tmp122*tmp74 + tmp123*tmp146 + tmp123*tmp154 + 2*tmp123*tmp155 + tmp126*tmp152 + tmp127*tmp156*tmp162 + tmp127*tmp91 + tmp128*tmp146 + tmp128*tmp154 + 2*tmp129*tmp158 + tmp131*tmp157 - tmp133*tmp47 - tmp134*(tmp123*tmp42 + tmp129*tmp45 + tmp130*tmp160*tmp22 + tmp159*tmp27 + tmp161*tmp37 + tmp29*tmp32) + tmp142*tmp160 + tmp149*tmp7 + tmp150*tmp159 + tmp151*tmp70 + tmp152*tmp34 + tmp157*tmp29 + tmp159*tmp93 - tmp19*tmp70 - tmp21*tmp7 + tmp33*tmp78);
aux_gfs[IDX4S(MU2GF, i0, i1, i2)] = tmp0*tmp54*(tmp101*((tmp62)*(tmp62)) + tmp109*tmp39 + tmp119*tmp123 + tmp121*tmp34 + 2*tmp122*tmp146 + tmp122*tmp154 + tmp122*tmp155 + tmp123*tmp158 + tmp126*tmp157 + tmp128*tmp158 - tmp134*(tmp122*tmp125 + tmp123*tmp132 + tmp124*tmp159 + tmp124*tmp165 + tmp125*tmp164 + tmp128*tmp132 + tmp130*tmp161 + tmp148*tmp27*tmp50 + tmp32*tmp34) + tmp143*tmp50 + tmp148*tmp94 + 2*tmp149*tmp24 + tmp151*tmp162 + tmp152*tmp166 + tmp152*tmp25 + tmp154*tmp164 + tmp155*tmp164 + tmp156*tmp159 + tmp156*tmp165 + tmp157*tmp34 + tmp159*tmp90 + tmp163*tmp74 - tmp19*tmp50 - tmp21*tmp24 + tmp25*tmp77 - tmp47*(tmp122*tmp124 + tmp123*tmp130 + tmp124*tmp164 + tmp125*tmp166 + tmp125*tmp25 + tmp126*tmp132 + tmp132*tmp34 + tmp163*tmp27 + tmp32*tmp39));
}
} // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++)
} // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++)
} // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++)
}
const int Nxx0 = params->Nxx0;
const int Nxx1 = params->Nxx1;
const int Nxx2 = params->Nxx2;
const int Nxx_plus_2NGHOSTS0 = params->Nxx_plus_2NGHOSTS0;
const int Nxx_plus_2NGHOSTS1 = params->Nxx_plus_2NGHOSTS1;
const int Nxx_plus_2NGHOSTS2 = params->Nxx_plus_2NGHOSTS2;
const REAL NOSIMDdxx0 = params->dxx0;
const REAL_SIMD_ARRAY dxx0 = ConstSIMD(NOSIMDdxx0);
const REAL NOSIMDdxx1 = params->dxx1;
const REAL_SIMD_ARRAY dxx1 = ConstSIMD(NOSIMDdxx1);
const REAL NOSIMDdxx2 = params->dxx2;
const REAL_SIMD_ARRAY dxx2 = ConstSIMD(NOSIMDdxx2);
const REAL NOSIMDinvdx0 = params->invdx0;
const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0);
const REAL NOSIMDinvdx1 = params->invdx1;
const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1);
const REAL NOSIMDinvdx2 = params->invdx2;
const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2);
const REAL NOSIMDRMAX = params->RMAX;
const REAL_SIMD_ARRAY RMAX = ConstSIMD(NOSIMDRMAX);
const REAL NOSIMDM = params->M;
const REAL_SIMD_ARRAY M = ConstSIMD(NOSIMDM);
const REAL NOSIMDa = params->a;
const REAL_SIMD_ARRAY a = ConstSIMD(NOSIMDa);
const REAL NOSIMDr0 = params->r0;
const REAL_SIMD_ARRAY r0 = ConstSIMD(NOSIMDr0);
const int Nxx0 = params.Nxx0;
const int Nxx1 = params.Nxx1;
const int Nxx2 = params.Nxx2;
const int Nxx_plus_2NGHOSTS0 = params.Nxx_plus_2NGHOSTS0;
const int Nxx_plus_2NGHOSTS1 = params.Nxx_plus_2NGHOSTS1;
const int Nxx_plus_2NGHOSTS2 = params.Nxx_plus_2NGHOSTS2;
const REAL dxx0 = params.dxx0;
const REAL dxx1 = params.dxx1;
const REAL dxx2 = params.dxx2;
const REAL invdx0 = params.invdx0;
const REAL invdx1 = params.invdx1;
const REAL invdx2 = params.invdx2;
const REAL RMAX = params.RMAX;
const REAL M = params.M;
const REAL a = params.a;
const REAL r0 = params.r0;
const int Nxx0 = params->Nxx0;
const int Nxx1 = params->Nxx1;
const int Nxx2 = params->Nxx2;
const int Nxx_plus_2NGHOSTS0 = params->Nxx_plus_2NGHOSTS0;
const int Nxx_plus_2NGHOSTS1 = params->Nxx_plus_2NGHOSTS1;
const int Nxx_plus_2NGHOSTS2 = params->Nxx_plus_2NGHOSTS2;
const REAL dxx0 = params->dxx0;
const REAL dxx1 = params->dxx1;
const REAL dxx2 = params->dxx2;
const REAL invdx0 = params->invdx0;
const REAL invdx1 = params->invdx1;
const REAL invdx2 = params->invdx2;
const REAL RMAX = params->RMAX;
const REAL M = params->M;
const REAL a = params->a;
const REAL r0 = params->r0;
params.Nxx0 = 64;
params.Nxx1 = 32;
params.Nxx2 = 64;
params.Nxx_plus_2NGHOSTS0 = 70;
params.Nxx_plus_2NGHOSTS1 = 38;
params.Nxx_plus_2NGHOSTS2 = 70;
params.xx0 = 1e+300;
params.xx1 = 1e+300;
params.xx2 = 1e+300;
params.dxx0 = 0.1;
params.dxx1 = 0.1;
params.dxx2 = 0.1;
params.invdx0 = 1.0;
params.invdx1 = 1.0;
params.invdx2 = 1.0;
params.f0_of_xx0 = 1e+300;
params.f1_of_xx1 = 1e+300;
params.f2_of_xx0_xx1 = 1e+300;
params.f3_of_xx0 = 1e+300;
params.f4_of_xx2 = 1e+300;
params.f0_of_xx0__D0 = 1e+300;
params.f0_of_xx0__DD00 = 1e+300;
params.f0_of_xx0__DDD000 = 1e+300;
params.f1_of_xx1__D1 = 1e+300;
params.f1_of_xx1__DD11 = 1e+300;
params.f1_of_xx1__DDD111 = 1e+300;
params.f2_of_xx0_xx1__D0 = 1e+300;
params.f2_of_xx0_xx1__D1 = 1e+300;
params.f2_of_xx0_xx1__DD00 = 1e+300;
params.f2_of_xx0_xx1__DD11 = 1e+300;
params.f3_of_xx0__D0 = 1e+300;
params.f3_of_xx0__DD00 = 1e+300;
params.f4_of_xx2__D2 = 1e+300;
params.f4_of_xx2__DD22 = 1e+300;
params.RMAX = 10.0;
params.M = 1.0;
params.a = 0.9;
params.r0 = 1.0;
void set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3],
paramstruct *restrict params, REAL *restrict xx[3]) {
// Override parameter defaults with values based on command line arguments and NGHOSTS.
params->Nxx0 = Nxx[0];
params->Nxx1 = Nxx[1];
params->Nxx2 = Nxx[2];
params->Nxx_plus_2NGHOSTS0 = Nxx[0] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS1 = Nxx[1] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS2 = Nxx[2] + 2*NGHOSTS;
// Step 0d: Set up space and time coordinates
// Step 0d.i: Declare \Delta x^i=dxx{0,1,2} and invdxx{0,1,2}, as well as xxmin[3] and xxmax[3]:
#include "set_Cparameters.h"
REAL xxmin[3],xxmax[3];
if(EigenCoord == 0) {
xxmin[0] = 0;
xxmax[0] = RMAX;
xxmin[1] = 0;
xxmax[1] = M_PI;
xxmin[2] = -M_PI;
xxmax[2] = M_PI;
} else if (EigenCoord == 1) {
xxmin[0] = 0;
xxmax[0] = RMAX;
xxmin[1] = 0;
xxmax[1] = M_PI;
xxmin[2] = -M_PI;
xxmax[2] = M_PI;
}
params->dxx0 = (xxmax[0] - xxmin[0]) / ((REAL)Nxx[0]);
params->dxx1 = (xxmax[1] - xxmin[1]) / ((REAL)Nxx[1]);
params->dxx2 = (xxmax[2] - xxmin[2]) / ((REAL)Nxx[2]);
params->invdx0 = 1.0/params->dxx0;
params->invdx1 = 1.0/params->dxx1;
params->invdx2 = 1.0/params->dxx2;
// Now that params.dxx{0,1,2} and params.invdxx{0,1,2} have been set,
// Step 0d.iii: Set up uniform coordinate grids
xx[0] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS0);
for(int j=0;j<Nxx_plus_2NGHOSTS0;j++)
xx[0][j] = xxmin[0] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx0; // Cell-centered grid.
xx[1] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS1);
for(int j=0;j<Nxx_plus_2NGHOSTS1;j++)
xx[1][j] = xxmin[1] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx1; // Cell-centered grid.
xx[2] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS2);
for(int j=0;j<Nxx_plus_2NGHOSTS2;j++)
xx[2][j] = xxmin[2] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx2; // Cell-centered grid.
//fprintf(stderr,"hey inside setxx: %e %e %e | %e %e\n",xxmin[0],xxmin[1],xxmin[2],xx[0][0],params->dxx0);
}
inline void xxCart(const paramstruct *restrict params, REAL *restrict xx[3],const int i0,const int i1,const int i2, REAL xCart[3]) {
#include "./set_Cparameters.h"
REAL xx0 = xx[0][i0];
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];
/*
* Original SymPy expressions:
* "[xCart[0] = xx0*sin(xx1)*cos(xx2),
* xCart[1] = xx0*sin(xx1)*sin(xx2),
* xCart[2] = xx0*cos(xx1)]"
*/
{
const double tmp0 = xx0*sin(xx1);
xCart[0] = tmp0*cos(xx2);
xCart[1] = tmp0*sin(xx2);
xCart[2] = xx0*cos(xx1);
}
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
"<script>\n",
" window.dataLayer = window.dataLayer || [];\n",
" function gtag(){dataLayer.push(arguments);}\n",
" gtag('js', new Date());\n",
"\n",
" gtag('config', 'UA-59152712-8');\n",
"</script>\n",
"\n",
"# Start-to-Finish Example: Setting up Exact Initial Data for Einstein's Equations, in Curvilinear Coordinates\n",
"## Authors: Brandon Clark, George Vopal, and Zach Etienne\n",
"\n",
"## This module sets up initial data for a specified exact solution written in terms of ADM variables, using the [*Exact* ADM Spherical to BSSN Curvilinear initial data module](../edit/BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py).\n",
"\n",
"**Notebook Status:** <font color='green'><b> Validated </b></font>\n",
"\n",
"**Validation Notes:** This module has been validated, confirming that all initial data sets exhibit convergence to zero of the Hamiltonian and momentum constraints at the expected rate or better.\n",
"\n",
"### NRPy+ Source Code for this module:\n",
"* [BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py](../edit/BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb): *Exact* Spherical ADM$\\to$Curvilinear BSSN converter function\n",
"* [BSSN/BSSN_constraints.py](../edit/BSSN/BSSN_constraints.py); [\\[**tutorial**\\]](Tutorial-BSSN_constraints.ipynb): Hamiltonian & momentum constraints in BSSN curvilinear basis/coordinates\n",
"\n",
"## Introduction:\n",
"Here we use NRPy+ to generate a C code confirming that specified *exact* initial data satisfy Einstein's equations of general relativity. The following exact initial data types are supported:\n",
"\n",
"* Shifted Kerr-Schild spinning black hole initial data\n",
"* \"Static\" Trumpet black hole initial data\n",
"* Brill-Lindquist two black hole initial data\n",
"* UIUC black hole initial data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='toc'></a>\n",
"\n",
"# Table of Contents\n",
"$$\\label{toc}$$\n",
"\n",
"This notebook is organized as follows\n",
"\n",
"0. [Preliminaries](#prelim): The Choices for Initial Data\n",
" 1. [Choice 1](#sks): Shifted Kerr-Schild spinning black hole initial data\n",
" 1. [Choice 2](#st): \"Static\" Trumpet black hole initial data\n",
" 1. [Choice 3](#bl): Brill-Lindquist two black hole initial data\n",
" 1. [Choice 4](#uiuc): UIUC black hole initial data\n",
"1. [Step 2](#initializenrpy): Set core NRPy+ parameters for numerical grids and reference metric\n",
"1. [Step 3](#adm_id): Import Black Hole ADM initial data C function from NRPy+ module\n",
"1. [Step 4](#validate): Validating that the black hole initial data satisfy the Hamiltonian constraint\n",
" 1. [Step 4.a](#ham_const_output): Output C code for evaluating the Hamiltonian and Momentum constraint violation\n",
" 1. [Step 4.b](#apply_bcs): Apply singular, curvilinear coordinate boundary conditions\n",
" 1. [Step 4.c](#enforce3metric): Enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint\n",
"1. [Step 5](#mainc): `Initial_Data.c`: The Main C Code\n",
"1. [Step 6](#plot): Plotting the initial data\n",
"1. [Step 7](#convergence): Validation: Convergence of numerical errors (Hamiltonian constraint violation) to zero\n",
"1. [Step 8](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='prelim'></a>\n",
"\n",
"# Preliminaries: The Choices for Initial Data\n",
"$$\\label{prelim}$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='sks'></a>\n",
"\n",
"## Shifted Kerr-Schild spinning black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{sks}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for a spinning black hole.\n",
"\n",
"Shifted Kerr-Schild spinning black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of both the Hamiltonian and momentum constraint violations at the expected order to the exact solution.\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/ShiftedKerrSchild.py](../edit/BSSN/ShiftedKerrSchild.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-ShiftedKerrSchild.ipynb)\n",
"\n",
"The [BSSN.ShiftedKerrSchild](../edit/BSSN/ShiftedKerrSchild.py) NRPy+ module does the following:\n",
"\n",
"1. Set up shifted Kerr-Schild initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-ShiftedKerrSchild.ipynb). \n",
"1. Convert the exact ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='st'></a>\n",
"\n",
"## \"Static\" Trumpet black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{st}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for a single trumpet black hole ([Dennison & Baumgarte, PRD ???](https://arxiv.org/abs/??)).\n",
"\n",
"\"Static\" Trumpet black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution. It was carefully ported from the [original NRPy+ code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/StaticTrumpet.py](../edit/BSSN/StaticTrumpet.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-StaticTrumpet.ipynb)\n",
"\n",
"The [BSSN.StaticTrumpet](../edit/BSSN/StaticTrumpet.py) NRPy+ module does the following:\n",
"\n",
"1. Set up static trumpet black hole initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-StaticTrumpetBlackHoleipynb). \n",
"1. Convert the exact ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='bl'></a>\n",
"\n",
"## Brill-Lindquist initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{bl}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for two black holes (Brill-Lindquist, [Brill & Lindquist, Phys. Rev. 131, 471, 1963](https://journals.aps.org/pr/abstract/10.1103/PhysRev.131.471); see also Eq. 1 of [Brandt & Brügmann, arXiv:gr-qc/9711015v1](https://arxiv.org/pdf/gr-qc/9711015v1.pdf)).\n",
"\n",
"[//]: # \" and then we use it to generate the RHS expressions for [Method of Lines](https://reference.wolfram.com/language/tutorial/NDSolveMethodOfLines.html) time integration based on the [explicit Runge-Kutta fourth-order scheme](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods) (RK4).\"\n",
"\n",
"Brill-Lindquist initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution, and all quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/BrillLindquist.py](../edit/BSSN/BrillLindquist.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-Brill-Lindquist.ipynb)\n",
"* [BSSN/BSSN_ID_function_string.py](../edit/BSSN/BSSN_ID_function_string.py)\n",
"\n",
"The [BSSN.BrillLindquist](../edit/BSSN/BrillLindquist.py) NRPy+ module does the following:\n",
"\n",
"1. Set up Brill-Lindquist initial data [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Cartesian basis**, as [documented here](Tutorial-ADM_Initial_Data-Brill-Lindquist.ipynb). \n",
"1. Convert the ADM **Cartesian quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_ADMCartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='uiuc'></a>\n",
"\n",
"## UIUC black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{uiuc}$$ \n",
"\n",
"UIUC black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution, and all quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/UIUCBlackHole.py](../edit/BSSN/UIUCBlackHole.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-UIUCBlackHole.ipynb)\n",
"\n",
"The [BSSN.UIUCBlackHole](../edit/BSSN/UIUCBlackHole.py) NRPy+ module does the following:\n",
"\n",
"1. Set up UIUC black hole initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-UIUCBlackHoleipynb). \n",
"1. Convert the numerical ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='-pickid'></a>\n",
"\n",
"# Step 1: Specify the Initial Data to Test \\[Back to [top](#toc)\\]\n",
"$$\\label{pickid}$$\n",
"\n",
"Here you have a choice for which initial data you would like to import and test for convergence. The following is a list of the currently compatible `initial_data_string` options for you to choose from.\n",
"\n",
"* `\"Shifted KerrSchild\"`\n",
"* `\"Static Trumpet\"`\n",
"* `\"Brill-Lindquist\"`\n",
"* `\"UIUC\"`"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import collections\n",
"\n",
"#################\n",
"# For the User: Choose initial data, default is Shifted KerrSchild.\n",
"# You are also encouraged to adjust any of the \n",
"# DestGridCoordSystem, freeparams, or EnableMomentum parameters! \n",
"# NOTE: Only DestGridCoordSystem == Spherical or SinhSpherical\n",
"# currently work out of the box; additional modifications\n",
"# will likely be necessary for other CoordSystems.\n",
"#################\n",
"initial_data_string = \"Shifted KerrSchild\" # \"UIUC\"\n",
"\n",
"\n",
"dictID = {}\n",
"IDmod_retfunc = collections.namedtuple('IDmod_retfunc', 'modulename functionname DestGridCoordSystem freeparams EnableMomentum')\n",
"\n",
"dictID['Shifted KerrSchild'] = IDmod_retfunc(\n",
" modulename = \"BSSN.ShiftedKerrSchild\", functionname = \"ShiftedKerrSchild\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL M = 1.0;\", \"const REAL a = 0.9;\", \"const REAL r0 = 1.0;\"], \n",
" EnableMomentum = True)\n",
"\n",
"dictID['Static Trumpet'] = IDmod_retfunc(\n",
" modulename = \"BSSN.StaticTrumpet\", functionname = \"StaticTrumpet\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL M = 1.0;\"], \n",
" EnableMomentum = False)\n",
"\n",
"dictID['Brill-Lindquist'] = IDmod_retfunc(\n",
" modulename = \"BSSN.BrillLindquist\", functionname = \"BrillLindquist\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL BH1_posn_x =-1.0,BH1_posn_y = 0.0,BH1_posn_z = 0.0;\",\n",
" \"const REAL BH2_posn_x = 1.0,BH2_posn_y = 0.0,BH2_posn_z = 0.0;\", \"const REAL BH1_mass = 0.5,BH2_mass = 0.5;\"], \n",
" EnableMomentum = False)\n",
"\n",
"dictID['UIUC'] = IDmod_retfunc(modulename = \"BSSN.UIUCBlackHole\", functionname = \"UIUCBlackHole\", \n",
" DestGridCoordSystem = \"SinhSpherical\",\n",
" freeparams = [\"const REAL M = 1.0;\", \"const REAL chi = 0.99;\"], \n",
" EnableMomentum = True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='initializenrpy'></a>\n",
"\n",
"# Step 2: Set up the needed NRPy+ infrastructure and declare core gridfunctions \\[Back to [top](#toc)\\]\n",
"$$\\label{initializenrpy}$$\n",
"\n",
"We will import the core modules of NRPy that we will need and specify the main gridfunctions we will need."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Step P1: Import needed NRPy+ core modules:\n",
"import sys\n",
"sys.path.append(\"nrpytutorial\")\n",
"from outputC import * # NRPy+: Core C code output module\n",
"import finite_difference as fin # NRPy+: Finite difference C code generation module\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 cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n",
"import shutil, os, time # Standard Python modules for multiplatform OS-level functions, benchmarking\n",
"import importlib # Standard Python module for interactive module imports\n",
"\n",
"# Step P2: Create C code output directory:\n",
"Ccodesdir = os.path.join(\"BlackHoleID_Ccodes/\")\n",
"# First remove C code output directory if it exists\n",
"# Courtesy https://stackoverflow.com/questions/303200/how-do-i-remove-delete-a-folder-that-is-not-empty\n",
"# !rm -r ScalarWaveCurvilinear_Playground_Ccodes\n",
"shutil.rmtree(Ccodesdir, ignore_errors=True)\n",
"# Then create a fresh directory\n",
"cmd.mkdir(Ccodesdir)\n",
"\n",
"# Step P3: Create executable output directory:\n",
"outdir = os.path.join(Ccodesdir,\"output/\")\n",
"cmd.mkdir(outdir)\n",
"\n",
"# Step 1: Set the spatial dimension parameter \n",
"# to three this time, and then read\n",
"# the parameter as DIM.\n",
"par.set_parval_from_str(\"grid::DIM\",3)\n",
"DIM = par.parval_from_str(\"grid::DIM\")\n",
"\n",
"# Step 2: Set some core parameters, including CoordSystem MoL timestepping algorithm,\n",
"# FD order, floating point precision, and CFL factor:\n",
"# Choices are: Spherical, SinhSpherical, SinhSphericalv2, Cylindrical, SinhCylindrical, \n",
"# SymTP, SinhSymTP\n",
"CoordSystem = \"Spherical\"\n",
"\n",
"# Step 2.a: Set defaults for Coordinate system parameters.\n",
"# These are perhaps the most commonly adjusted parameters,\n",
"# so we enable modifications at this high level.\n",
"\n",
"# domain_size sets the default value for:\n",
"# * Spherical's params.RMAX\n",
"# * SinhSpherical*'s params.AMAX\n",
"# * Cartesians*'s -params.{x,y,z}min & .{x,y,z}max\n",
"# * Cylindrical's -params.ZMIN & .{Z,RHO}MAX\n",
"# * SinhCylindrical's params.AMPL{RHO,Z}\n",
"# * *SymTP's params.AMAX\n",
"domain_size = 3.0\n",
"\n",
"# sinh_width sets the default value for:\n",
"# * SinhSpherical's params.SINHW\n",
"# * SinhCylindrical's params.SINHW{RHO,Z}\n",
"# * SinhSymTP's params.SINHWAA\n",
"sinh_width = 0.4 # If Sinh* coordinates chosen\n",
"\n",
"# sinhv2_const_dr sets the default value for:\n",
"# * SinhSphericalv2's params.const_dr\n",
"# * SinhCylindricalv2's params.const_d{rho,z}\n",
"sinhv2_const_dr = 0.05# If Sinh*v2 coordinates chosen\n",
"\n",
"# SymTP_bScale sets the default value for:\n",
"# * SinhSymTP's params.bScale\n",
"SymTP_bScale = 0.5 # If SymTP chosen\n",
"\n",
"FD_order = 4 # Finite difference order: even numbers only, starting with 2. 12 is generally unstable\n",
"REAL = \"double\" # Best to use double here.\n",
"\n",
"# Step 3: Set the coordinate system for the numerical grid\n",
"par.set_parval_from_str(\"reference_metric::CoordSystem\",CoordSystem)\n",
"rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.\n",
"\n",
"# Step 4: Set the finite differencing order to FD_order (set above).\n",
"par.set_parval_from_str(\"finite_difference::FD_CENTDERIVS_ORDER\", FD_order)\n",
"\n",
"# Step 5: Set the direction=2 (phi) axis to be the symmetry axis; i.e., \n",
"# axis \"2\", corresponding to the i2 direction. \n",
"# This sets all spatial derivatives in the phi direction to zero.\n",
"par.set_parval_from_str(\"indexedexp::symmetry_axes\",\"2\")\n",
"\n",
"# Step 6: The MoLtimestepping interface is only used for memory allocation/deallocation\n",
"import MoLtimestepping.C_Code_Generation as MoL\n",
"from MoLtimestepping.RK_Butcher_Table_Dictionary import Butcher_dict\n",
"RK_method = \"Euler\" # DOES NOT MATTER; Again MoL interface is only used for memory alloc/dealloc.\n",
"RK_order = Butcher_dict[RK_method][1]\n",
"cmd.mkdir(os.path.join(Ccodesdir,\"MoLtimestepping/\"))\n",
"MoL.MoL_C_Code_Generation(RK_method, RHS_string = \"\", post_RHS_string = \"\",\n",
" outdir = os.path.join(Ccodesdir,\"MoLtimestepping/\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='adm_id'></a>\n",
"\n",
"# Step 3: Import Black Hole ADM initial data C function from NRPy+ module \\[Back to [top](#toc)\\]\n",
"$$\\label{adm_id}$$"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Import Black Hole initial data\n",
"\n",
"IDmodule = importlib.import_module(dictID[initial_data_string].modulename)\n",
"IDfunc = getattr(IDmodule, dictID[initial_data_string].functionname)\n",
"IDfunc() # Registers ID C function in dictionary, used below to output to file.\n",
"with open(os.path.join(Ccodesdir,\"initial_data.h\"),\"w\") as file:\n",
" file.write(outC_function_dict[\"initial_data\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='cparams_rfm_and_domainsize'></a>\n",
"\n",
"## Step 3.a: Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h` \\[Back to [top](#toc)\\]\n",
"$$\\label{cparams_rfm_and_domainsize}$$\n",
"\n",
"Based on declared NRPy+ Cparameters, first we generate `declare_Cparameters_struct.h`, `set_Cparameters_default.h`, and `set_Cparameters[-SIMD].h`.\n",
"\n",
"Then we output `free_parameters.h`, which sets initial data parameters, as well as grid domain & reference metric parameters, applying `domain_size` and `sinh_width`/`SymTP_bScale` (if applicable) as set above"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Step 3.a.i: Set free_parameters.h\n",
"# Output to $Ccodesdir/free_parameters.h reference metric parameters based on generic\n",
"# domain_size,sinh_width,sinhv2_const_dr,SymTP_bScale,\n",
"# parameters set above. \n",
"rfm.out_default_free_parameters_for_rfm(os.path.join(Ccodesdir,\"free_parameters.h\"),\n",
" domain_size,sinh_width,sinhv2_const_dr,SymTP_bScale)\n",
"\n",
"# Step 3.a.ii: Generate set_Nxx_dxx_invdx_params__and__xx.h:\n",
"rfm.set_Nxx_dxx_invdx_params__and__xx_h(Ccodesdir)\n",
"\n",
"# Step 3.a.iii: Generate xxCart.h, which contains xxCart() for\n",
"# (the mapping from xx->Cartesian) for the chosen\n",
"# CoordSystem:\n",
"rfm.xxCart_h(\"xxCart\",\"./set_Cparameters.h\",os.path.join(Ccodesdir,\"xxCart.h\"))\n",
"\n",
"# Step 3.a.iv: Generate declare_Cparameters_struct.h, set_Cparameters_default.h, and set_Cparameters[-SIMD].h\n",
"par.generate_Cparameters_Ccodes(os.path.join(Ccodesdir))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='validate'></a>\n",
"\n",
"# Step 4: Validating that the black hole initial data satisfy the Hamiltonian constraint \\[Back to [top](#toc)\\]\n",
"$$\\label{validate}$$\n",
"\n",
"We will validate that the black hole initial data satisfy the Hamiltonian constraint, modulo numerical finite differencing error."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='ham_const_output'></a>\n",
"\n",
"## Step 4.a: Output C code for evaluating the Hamiltonian and Momentum constraint violation \\[Back to [top](#toc)\\]\n",
"$$\\label{ham_const_output}$$\n",
"\n",
"First output C code for evaluating the Hamiltonian constraint violation. For the initial data where `EnableMomentum = True` we must also output C code for evaluating the Momentum constraint violation."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Output C function Hamiltonian_constraint() to file BlackHoleID_Ccodes/Hamiltonian_constraint.h\n",
"Output C function momentum_constraint() to file BlackHoleID_Ccodes/momentum_constraint.h\n"
]
}
],
"source": [
"import BSSN.BSSN_constraints as bssncon\n",
"# Now register the Hamiltonian & momentum constraints as gridfunctions.\n",
"H = gri.register_gridfunctions(\"AUX\",\"H\")\n",
"MU = ixp.register_gridfunctions_for_single_rank1(\"AUX\", \"MU\")\n",
"\n",
"# Generate symbolic expressions for Hamiltonian & momentum constraints\n",
"import BSSN.BSSN_constraints as bssncon\n",
"bssncon.BSSN_constraints(add_T4UUmunu_source_terms=False)\n",
"\n",
"# Generate otpimized C code for Hamiltonian constraint\n",
"desc=\"Evaluate the Hamiltonian constraint\"\n",
"name=\"Hamiltonian_constraint\"\n",
"outCfunction(\n",
" outfile = os.path.join(Ccodesdir,name+\".h\"), desc=desc, name=name,\n",
" params = \"\"\"const paramstruct *restrict params, REAL *restrict xx[3],\n",
" REAL *restrict in_gfs, REAL *restrict aux_gfs\"\"\",\n",
" body = fin.FD_outputC(\"returnstring\",lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"H\"), rhs=bssncon.H),\n",
" params=\"outCverbose=False\").replace(\"IDX4\",\"IDX4S\"),\n",
" loopopts = \"InteriorPoints,Read_xxs\")\n",
"\n",
"# Generate otpimized C code for momentum constraint\n",
"desc=\"Evaluate the momentum constraint\"\n",
"name=\"momentum_constraint\"\n",
"outCfunction(\n",
" outfile = os.path.join(Ccodesdir,name+\".h\"), desc=desc, name=name,\n",
" params = \"\"\"const paramstruct *restrict params, REAL *restrict xx[3],\n",
" REAL *restrict in_gfs, REAL *restrict aux_gfs\"\"\",\n",
" body = fin.FD_outputC(\"returnstring\",\n",
" [lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU0\"), rhs=bssncon.MU[0]),\n",
" lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU1\"), rhs=bssncon.MU[1]),\n",
" lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU2\"), rhs=bssncon.MU[2])],\n",
" params=\"outCverbose=False\").replace(\"IDX4\",\"IDX4S\"),\n",
" loopopts = \"InteriorPoints,Read_xxs\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='enforce3metric'></a>\n",
"\n",
"## Step 4.b: Enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint \\[Back to [top](#toc)\\]\n",
"$$\\label{enforce3metric}$$\n",
"\n",
"Then enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint (Eq. 53 of [Ruchlin, Etienne, and Baumgarte (2018)](https://arxiv.org/abs/1712.07658)), as [documented in the corresponding NRPy+ tutorial notebook](Tutorial-BSSN-Enforcing_Determinant_gammabar_equals_gammahat_Constraint.ipynb)\n",
"\n",
"Applying curvilinear boundary conditions should affect the initial data at the outer boundary, and will in general cause the $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint to be violated there. Thus after we apply these boundary conditions, we must always call the routine for enforcing the $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Output C function enforce_detgammabar_constraint() to file BlackHoleID_Ccodes/enforce_detgammabar_constraint.h\n"
]
}
],
"source": [
"# Set up the C function for the det(gammahat) = det(gammabar)\n",
"import BSSN.Enforce_Detgammabar_Constraint as EGC\n",
"enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammabar_Constraint_symb_expressions()\n",
"\n",
"EGC.output_Enforce_Detgammabar_Constraint_Ccode(Ccodesdir,exprs=enforce_detg_constraint_symb_expressions,\n",
" Read_xxs=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='bc_functs'></a>\n",
"\n",
"## Step 4.c: Set up boundary condition functions for chosen singular, curvilinear coordinate system \\[Back to [top](#toc)\\]\n",
"$$\\label{bc_functs}$$\n",
"\n",
"Next apply singular, curvilinear coordinate boundary conditions [as documented in the corresponding NRPy+ tutorial notebook](Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wrote to file \"BlackHoleID_Ccodes/boundary_conditions/parity_conditions_symbolic_dot_products.h\"\n",
"Evolved gridfunction \"aDD00\" has parity type 4.\n",
"Evolved gridfunction \"aDD01\" has parity type 5.\n",
"Evolved gridfunction \"aDD02\" has parity type 6.\n",
"Evolved gridfunction \"aDD11\" has parity type 7.\n",
"Evolved gridfunction \"aDD12\" has parity type 8.\n",
"Evolved gridfunction \"aDD22\" has parity type 9.\n",
"Evolved gridfunction \"alpha\" has parity type 0.\n",
"Evolved gridfunction \"betU0\" has parity type 1.\n",
"Evolved gridfunction \"betU1\" has parity type 2.\n",
"Evolved gridfunction \"betU2\" has parity type 3.\n",
"Evolved gridfunction \"cf\" has parity type 0.\n",
"Evolved gridfunction \"hDD00\" has parity type 4.\n",
"Evolved gridfunction \"hDD01\" has parity type 5.\n",
"Evolved gridfunction \"hDD02\" has parity type 6.\n",
"Evolved gridfunction \"hDD11\" has parity type 7.\n",
"Evolved gridfunction \"hDD12\" has parity type 8.\n",
"Evolved gridfunction \"hDD22\" has parity type 9.\n",
"Evolved gridfunction \"lambdaU0\" has parity type 1.\n",
"Evolved gridfunction \"lambdaU1\" has parity type 2.\n",
"Evolved gridfunction \"lambdaU2\" has parity type 3.\n",
"Evolved gridfunction \"trK\" has parity type 0.\n",
"Evolved gridfunction \"vetU0\" has parity type 1.\n",
"Evolved gridfunction \"vetU1\" has parity type 2.\n",
"Evolved gridfunction \"vetU2\" has parity type 3.\n",
"Auxiliary gridfunction \"H\" has parity type 0.\n",
"Auxiliary gridfunction \"MU0\" has parity type 1.\n",
"Auxiliary gridfunction \"MU1\" has parity type 2.\n",
"Auxiliary gridfunction \"MU2\" has parity type 3.\n",
"Wrote to file \"BlackHoleID_Ccodes/boundary_conditions/EigenCoord_Cart_to_xx.h\"\n"
]
}
],
"source": [
"import CurviBoundaryConditions.CurviBoundaryConditions as cbcs\n",
"cbcs.Set_up_CurviBoundaryConditions(os.path.join(Ccodesdir,\"boundary_conditions/\"),Cparamspath=os.path.join(\"../\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='mainc'></a>\n",
"\n",
"# Step 5: `Initial_Data_Playground.c`: The Main C Code \\[Back to [top](#toc)\\]\n",
"$$\\label{mainc}$$"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Part P0: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER\n",
"# set REAL=double, so that all floating point numbers are stored to at least ~16 significant digits.\n",
"\n",
"with open(os.path.join(Ccodesdir,\"Initial_Data_Playground_REAL__NGHOSTS.h\"), \"w\") as file:\n",
" file.write(\"\"\"\n",
"// Part P0.a: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER\n",
"#define NGHOSTS \"\"\"+str(int(par.parval_from_str(\"finite_difference::FD_CENTDERIVS_ORDER\")/2)+1)+\"\"\"\\n\n",
"// Part P0.b: Set the numerical precision (REAL) to double, ensuring all floating point\n",
"// numbers are stored to at least ~16 significant digits\n",
"#define REAL double\\n\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Writing BlackHoleID_Ccodes//Initial_Data_Playground.c\n"
]
}
],
"source": [
"with open(os.path.join(Ccodesdir,\"Initial_Data_Playground.c\"), \"w\") as file:\n",
" file.write(\"\"\"\n",
"\n",
"// Step P0: Define REAL and NGHOSTS. This header is generated by NRPy+.\n",
"#include \"Initial_Data_Playground_REAL__NGHOSTS.h\"\n",
"\n",
"#include \"declare_Cparameters_struct.h\"\n",
"\n",
"// Step P1: Import needed header files\n",
"#include \"stdio.h\"\n",
"#include \"stdlib.h\"\n",
"#include \"math.h\"\n",
"#ifndef M_PI\n",
"#define M_PI 3.141592653589793238462643383279502884L\n",
"#endif\n",
"#ifndef M_SQRT1_2\n",
"#define M_SQRT1_2 0.707106781186547524400844362104849039L\n",
"#endif\n",
"\n",
"// Step P2: Declare the IDX4S(gf,i,j,k) macro, which enables us to store 4-dimensions of\n",
"// data in a 1D array. In this case, consecutive values of \"i\" \n",
"// (all other indices held to a fixed value) are consecutive in memory, where \n",
"// consecutive values of \"j\" (fixing all other indices) are separated by \n",
"// Nxx_plus_2NGHOSTS0 elements in memory. Similarly, consecutive values of\n",
"// \"k\" are separated by Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1 in memory, etc.\n",
"#define IDX4S(g,i,j,k) \\\n",
"( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) + Nxx_plus_2NGHOSTS2 * (g) ) ) )\n",
"#define IDX4ptS(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2) * (g) )\n",
"#define IDX3S(i,j,k) ( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) ) ) )\n",
"#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) \\\n",
" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)\n",
"#define LOOP_ALL_GFS_GPS(ii) _Pragma(\"omp parallel for\") \\\n",
" for(int (ii)=0;(ii)<Nxx_plus_2NGHOSTS_tot*NUM_EVOL_GFS;(ii)++)\n",
"\n",
"// Step P3: Set UUGF and VVGF macros, as well as xxCart()\n",
"#include \"boundary_conditions/gridfunction_defines.h\"\n",
"\n",
"// Step P4: Set xxCart(const paramstruct *restrict params, \n",
"// REAL *restrict xx[3],\n",
"// const int i0,const int i1,const int i2, \n",
"// REAL xCart[3]),\n",
"// which maps xx->Cartesian via\n",
"// {xx[0][i0],xx[1][i1],xx[2][i2]}->{xCart[0],xCart[1],xCart[2]}\n",
"#include \"xxCart.h\"\n",
"\n",
"// Step P5: Defines set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3], \n",
"// paramstruct *restrict params, REAL *restrict xx[3]),\n",
"// which sets params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for\n",
"// the chosen Eigen-CoordSystem if EigenCoord==1, or\n",
"// CoordSystem if EigenCoord==0.\n",
"#include \"set_Nxx_dxx_invdx_params__and__xx.h\"\n",
"\n",
"// Step P6: Include basic functions needed to impose curvilinear\n",
"// parity and boundary conditions.\n",
"#include \"boundary_conditions/CurviBC_include_Cfunctions.h\"\n",
"\n",
"// Step P8: Include function for enforcing detgammabar constraint.\n",
"#include \"enforce_detgammabar_constraint.h\"\n",
"\n",
"// Step P10: Declare function necessary for setting up the initial data.\n",
"// Step P10.a: Define BSSN_ID() for BrillLindquist initial data\n",
"\n",
"// Step P10.b: Set the generic driver function for setting up BSSN initial data\n",
"#include \"initial_data.h\"\n",
"\n",
"// Step P11: Declare function for evaluating Hamiltonian constraint (diagnostic)\n",
"#include \"Hamiltonian_constraint.h\" \n",
"#include \"momentum_constraint.h\" \n",
"\n",
"// main() function:\n",
"// Step 0: Read command-line input, set up grid structure, allocate memory for gridfunctions, set up coordinates\n",
"// Step 1: Set up initial data to an exact solution\n",
"// Step 2: Start the timer, for keeping track of how fast the simulation is progressing.\n",
"// Step 3: Integrate the initial data forward in time using the chosen RK-like Method of \n",
"// Lines timestepping algorithm, and output periodic simulation diagnostics \n",
"// Step 3.a: Output 2D data file periodically, for visualization\n",
"// Step 3.b: Step forward one timestep (t -> t+dt) in time using \n",
"// chosen RK-like MoL timestepping algorithm\n",
"// Step 3.c: If t=t_final, output conformal factor & Hamiltonian \n",
"// constraint violation to 2D data file\n",
"// Step 3.d: Progress indicator printing to stderr\n",
"// Step 4: Free all allocated memory\n",
"int main(int argc, const char *argv[]) {\n",
" paramstruct params;\n",
"#include \"set_Cparameters_default.h\"\n",
" \n",
" // Step 0a: Read command-line input, error out if nonconformant\n",
" if((argc != 4) || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < 2 /* FIXME; allow for axisymmetric sims */) {\n",
" fprintf(stderr,\"Error: Expected three command-line arguments: ./BrillLindquist_Playground Nx0 Nx1 Nx2,\\n\");\n",
" fprintf(stderr,\"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.\\n\");\n",
" fprintf(stderr,\"Nx[] MUST BE larger than NGHOSTS (= %d)\\n\",NGHOSTS);\n",
" exit(1);\n",
" }\n",
" // Step 0b: Set up numerical grid structure, first in space...\n",
" const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };\n",
" if(Nxx[0]%2 != 0 || Nxx[1]%2 != 0 || Nxx[2]%2 != 0) {\n",
" fprintf(stderr,\"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.\\n\");\n",
" fprintf(stderr,\" For example, in case of angular directions, proper symmetry zones will not exist.\\n\");\n",
" exit(1);\n",
" }\n",
"\n",
" // Step 0c: Set free parameters, overwriting Cparameters defaults \n",
" // by hand or with command-line input, as desired.\n",
"#include \"free_parameters.h\"\n",
"\n",
" // Step 0d: Uniform coordinate grids are stored to *xx[3]\n",
" REAL *xx[3];\n",
" // Step 0d.i: Set bcstruct\n",
" bc_struct bcstruct;\n",
" {\n",
" int EigenCoord = 1;\n",
" // Step 0d.ii: Call set_Nxx_dxx_invdx_params__and__xx(), which sets\n",
" // params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the\n",
" // chosen Eigen-CoordSystem.\n",
" set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);\n",
" // Step 0d.iii: Set Nxx_plus_2NGHOSTS_tot\n",
"#include \"set_Cparameters-nopointer.h\"\n",
" const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;\n",
" // Step 0e: Find ghostzone mappings; set up bcstruct\n",
"#include \"boundary_conditions/driver_bcstruct.h\"\n",
" // Step 0e.i: Free allocated space for xx[][] array\n",
" for(int i=0;i<3;i++) free(xx[i]);\n",
" }\n",
" \n",
" // Step 0f: Call set_Nxx_dxx_invdx_params__and__xx(), which sets\n",
" // params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the\n",
" // chosen (non-Eigen) CoordSystem.\n",
" int EigenCoord = 0;\n",
" set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);\n",
"\n",
" // Step 0g: Set all C parameters \"blah\" for params.blah, including\n",
" // Nxx_plus_2NGHOSTS0 = params.Nxx_plus_2NGHOSTS0, etc.\n",
"#include \"set_Cparameters-nopointer.h\"\n",
" const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;\n",
"\n",
" // Step 0j: Error out if the number of auxiliary gridfunctions outnumber evolved gridfunctions.\n",
" // This is a limitation of the RK method. You are always welcome to declare & allocate \n",
" // additional gridfunctions by hand.\n",
" if(NUM_AUX_GFS > NUM_EVOL_GFS) {\n",
" fprintf(stderr,\"Error: NUM_AUX_GFS > NUM_EVOL_GFS. Either reduce the number of auxiliary gridfunctions,\\n\");\n",
" fprintf(stderr,\" or allocate (malloc) by hand storage for *diagnostic_output_gfs. \\n\");\n",
" exit(1);\n",
" }\n",
" \n",
" // Step 0k: Allocate memory for gridfunctions\n",
"#include \"MoLtimestepping/RK_Allocate_Memory.h\"\n",
" REAL *restrict auxevol_gfs = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot);\n",
" \n",
" // Step 1: Set up initial data to an exact solution\n",
" initial_data(¶ms, xx, y_n_gfs);\n",
"\n",
" // Step 1b: Apply boundary conditions, as initial data \n",
" // are sometimes ill-defined in ghost zones.\n",
" // E.g., spherical initial data might not be\n",
" // properly defined at points where r=-1.\n",
" apply_bcs_curvilinear(¶ms, &bcstruct, NUM_EVOL_GFS,evol_gf_parity, y_n_gfs);\n",
" enforce_detgammabar_constraint(¶ms, xx, y_n_gfs);\n",
"\n",
" // Evaluate Hamiltonian & momentum constraint violations\n",
" Hamiltonian_constraint(¶ms, xx, y_n_gfs, diagnostic_output_gfs);\n",
" momentum_constraint( ¶ms, xx, y_n_gfs, diagnostic_output_gfs);\n",
"\n",
" /* Step 2: 2D output: Output conformal factor (CFGF) and constraint violations (HGF, MU0GF, MU1GF, MU2GF). */\n",
" const int i0MIN=NGHOSTS; // In spherical, r=Delta r/2.\n",
" const int i1mid=Nxx_plus_2NGHOSTS1/2;\n",
" const int i2mid=Nxx_plus_2NGHOSTS2/2;\n",
" LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS0-NGHOSTS, i1mid,i1mid+1, NGHOSTS,Nxx_plus_2NGHOSTS2-NGHOSTS) {\n",
" REAL xCart[3];\n",
" xxCart(¶ms, xx, i0,i1,i2, xCart);\n",
" int idx = IDX3S(i0,i1,i2);\n",
" printf(\"%e %e %e %e %e %e %e\\n\",xCart[0],xCart[1], y_n_gfs[IDX4ptS(CFGF,idx)],\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(HGF,idx)])),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU0GF,idx)])+1e-200),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU1GF,idx)])+1e-200),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU2GF,idx)])+1e-200));\n",
" }\n",
" // Step 4: Free all allocated memory\n",
"#include \"boundary_conditions/bcstruct_freemem.h\"\n",
"#include \"MoLtimestepping/RK_Free_Memory.h\"\n",
" free(auxevol_gfs);\n",
" for(int i=0;i<3;i++) free(xx[i]);\n",
"\n",
" return 0;\n",
"}\\n\"\"\")"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
"<script>\n",
" window.dataLayer = window.dataLayer || [];\n",
" function gtag(){dataLayer.push(arguments);}\n",
" gtag('js', new Date());\n",
"\n",
" gtag('config', 'UA-59152712-8');\n",
"</script>\n",
"\n",
"# Start-to-Finish Example: Setting up Exact Initial Data for Einstein's Equations, in Curvilinear Coordinates\n",
"## Authors: Brandon Clark, George Vopal, and Zach Etienne\n",
"\n",
"## This module sets up initial data for a specified exact solution written in terms of ADM variables, using the [*Exact* ADM Spherical to BSSN Curvilinear initial data module](../edit/BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py).\n",
"\n",
"**Notebook Status:** <font color='green'><b> Validated </b></font>\n",
"\n",
"**Validation Notes:** This module has been validated, confirming that all initial data sets exhibit convergence to zero of the Hamiltonian and momentum constraints at the expected rate or better.\n",
"\n",
"### NRPy+ Source Code for this module:\n",
"* [BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py](../edit/BSSN/ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb): *Exact* Spherical ADM$\\to$Curvilinear BSSN converter function\n",
"* [BSSN/BSSN_constraints.py](../edit/BSSN/BSSN_constraints.py); [\\[**tutorial**\\]](Tutorial-BSSN_constraints.ipynb): Hamiltonian & momentum constraints in BSSN curvilinear basis/coordinates\n",
"\n",
"## Introduction:\n",
"Here we use NRPy+ to generate a C code confirming that specified *exact* initial data satisfy Einstein's equations of general relativity. The following exact initial data types are supported:\n",
"\n",
"* Shifted Kerr-Schild spinning black hole initial data\n",
"* \"Static\" Trumpet black hole initial data\n",
"* Brill-Lindquist two black hole initial data\n",
"* UIUC black hole initial data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='toc'></a>\n",
"\n",
"# Table of Contents\n",
"$$\\label{toc}$$\n",
"\n",
"This notebook is organized as follows\n",
"\n",
"0. [Preliminaries](#prelim): The Choices for Initial Data\n",
" 1. [Choice 1](#sks): Shifted Kerr-Schild spinning black hole initial data\n",
" 1. [Choice 2](#st): \"Static\" Trumpet black hole initial data\n",
" 1. [Choice 3](#bl): Brill-Lindquist two black hole initial data\n",
" 1. [Choice 4](#uiuc): UIUC black hole initial data\n",
"1. [Step 2](#initializenrpy): Set core NRPy+ parameters for numerical grids and reference metric\n",
"1. [Step 3](#adm_id): Import Black Hole ADM initial data C function from NRPy+ module\n",
"1. [Step 4](#validate): Validating that the black hole initial data satisfy the Hamiltonian constraint\n",
" 1. [Step 4.a](#ham_const_output): Output C code for evaluating the Hamiltonian and Momentum constraint violation\n",
" 1. [Step 4.b](#apply_bcs): Apply singular, curvilinear coordinate boundary conditions\n",
" 1. [Step 4.c](#enforce3metric): Enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint\n",
"1. [Step 5](#mainc): `Initial_Data.c`: The Main C Code\n",
"1. [Step 6](#plot): Plotting the initial data\n",
"1. [Step 7](#convergence): Validation: Convergence of numerical errors (Hamiltonian constraint violation) to zero\n",
"1. [Step 8](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='prelim'></a>\n",
"\n",
"# Preliminaries: The Choices for Initial Data\n",
"$$\\label{prelim}$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='sks'></a>\n",
"\n",
"## Shifted Kerr-Schild spinning black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{sks}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for a spinning black hole.\n",
"\n",
"Shifted Kerr-Schild spinning black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of both the Hamiltonian and momentum constraint violations at the expected order to the exact solution.\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/ShiftedKerrSchild.py](../edit/BSSN/ShiftedKerrSchild.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-ShiftedKerrSchild.ipynb)\n",
"\n",
"The [BSSN.ShiftedKerrSchild](../edit/BSSN/ShiftedKerrSchild.py) NRPy+ module does the following:\n",
"\n",
"1. Set up shifted Kerr-Schild initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-ShiftedKerrSchild.ipynb). \n",
"1. Convert the exact ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='st'></a>\n",
"\n",
"## \"Static\" Trumpet black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{st}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for a single trumpet black hole ([Dennison & Baumgarte, PRD ???](https://arxiv.org/abs/??)).\n",
"\n",
"\"Static\" Trumpet black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution. It was carefully ported from the [original NRPy+ code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/StaticTrumpet.py](../edit/BSSN/StaticTrumpet.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-StaticTrumpet.ipynb)\n",
"\n",
"The [BSSN.StaticTrumpet](../edit/BSSN/StaticTrumpet.py) NRPy+ module does the following:\n",
"\n",
"1. Set up static trumpet black hole initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-StaticTrumpetBlackHoleipynb). \n",
"1. Convert the exact ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='bl'></a>\n",
"\n",
"## Brill-Lindquist initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{bl}$$\n",
"\n",
"Here we use NRPy+ to generate initial data for two black holes (Brill-Lindquist, [Brill & Lindquist, Phys. Rev. 131, 471, 1963](https://journals.aps.org/pr/abstract/10.1103/PhysRev.131.471); see also Eq. 1 of [Brandt & Brügmann, arXiv:gr-qc/9711015v1](https://arxiv.org/pdf/gr-qc/9711015v1.pdf)).\n",
"\n",
"[//]: # \" and then we use it to generate the RHS expressions for [Method of Lines](https://reference.wolfram.com/language/tutorial/NDSolveMethodOfLines.html) time integration based on the [explicit Runge-Kutta fourth-order scheme](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods) (RK4).\"\n",
"\n",
"Brill-Lindquist initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution, and all quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/BrillLindquist.py](../edit/BSSN/BrillLindquist.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-Brill-Lindquist.ipynb)\n",
"* [BSSN/BSSN_ID_function_string.py](../edit/BSSN/BSSN_ID_function_string.py)\n",
"\n",
"The [BSSN.BrillLindquist](../edit/BSSN/BrillLindquist.py) NRPy+ module does the following:\n",
"\n",
"1. Set up Brill-Lindquist initial data [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Cartesian basis**, as [documented here](Tutorial-ADM_Initial_Data-Brill-Lindquist.ipynb). \n",
"1. Convert the ADM **Cartesian quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_ADMCartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='uiuc'></a>\n",
"\n",
"## UIUC black hole initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{uiuc}$$ \n",
"\n",
"UIUC black hole initial data has been <font color='green'><b> validated </b></font> to exhibit convergence to zero of the Hamiltonian constraint violation at the expected order to the exact solution, and all quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).\n",
"\n",
"**NRPy+ Source Code:**\n",
"* [BSSN/UIUCBlackHole.py](../edit/BSSN/UIUCBlackHole.py); [\\[**tutorial**\\]](Tutorial-ADM_Initial_Data-UIUCBlackHole.ipynb)\n",
"\n",
"The [BSSN.UIUCBlackHole](../edit/BSSN/UIUCBlackHole.py) NRPy+ module does the following:\n",
"\n",
"1. Set up UIUC black hole initial data, represented by [ADM](https://en.wikipedia.org/wiki/ADM_formalism) quantities in the **Spherical basis**, as [documented here](Tutorial-ADM_Initial_Data-UIUCBlackHoleipynb). \n",
"1. Convert the numerical ADM **Spherical quantities** to **BSSN quantities in the desired Curvilinear basis** (set by `reference_metric::CoordSystem`), as [documented here](Tutorial-ADM_Initial_Data-Converting_Numerical_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb).\n",
"1. Sets up the standardized C function for setting all BSSN Curvilinear gridfunctions in a pointwise fashion, as [written here](../edit/BSSN/BSSN_ID_function_string.py), and returns the C function as a Python string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='-pickid'></a>\n",
"\n",
"# Step 1: Specify the Initial Data to Test \\[Back to [top](#toc)\\]\n",
"$$\\label{pickid}$$\n",
"\n",
"Here you have a choice for which initial data you would like to import and test for convergence. The following is a list of the currently compatible `initial_data_string` options for you to choose from.\n",
"\n",
"* `\"Shifted KerrSchild\"`\n",
"* `\"Static Trumpet\"`\n",
"* `\"Brill-Lindquist\"`\n",
"* `\"UIUC\"`"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import collections\n",
"\n",
"#################\n",
"# For the User: Choose initial data, default is Shifted KerrSchild.\n",
"# You are also encouraged to adjust any of the \n",
"# DestGridCoordSystem, freeparams, or EnableMomentum parameters! \n",
"# NOTE: Only DestGridCoordSystem == Spherical or SinhSpherical\n",
"# currently work out of the box; additional modifications\n",
"# will likely be necessary for other CoordSystems.\n",
"#################\n",
"initial_data_string = \"Shifted KerrSchild\" # \"UIUC\"\n",
"\n",
"\n",
"dictID = {}\n",
"IDmod_retfunc = collections.namedtuple('IDmod_retfunc', 'modulename functionname DestGridCoordSystem freeparams EnableMomentum')\n",
"\n",
"dictID['Shifted KerrSchild'] = IDmod_retfunc(\n",
" modulename = \"BSSN.ShiftedKerrSchild\", functionname = \"ShiftedKerrSchild\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL M = 1.0;\", \"const REAL a = 0.9;\", \"const REAL r0 = 1.0;\"], \n",
" EnableMomentum = True)\n",
"\n",
"dictID['Static Trumpet'] = IDmod_retfunc(\n",
" modulename = \"BSSN.StaticTrumpet\", functionname = \"StaticTrumpet\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL M = 1.0;\"], \n",
" EnableMomentum = False)\n",
"\n",
"dictID['Brill-Lindquist'] = IDmod_retfunc(\n",
" modulename = \"BSSN.BrillLindquist\", functionname = \"BrillLindquist\", \n",
" DestGridCoordSystem = \"Spherical\",\n",
" freeparams = [\"const REAL BH1_posn_x =-1.0,BH1_posn_y = 0.0,BH1_posn_z = 0.0;\",\n",
" \"const REAL BH2_posn_x = 1.0,BH2_posn_y = 0.0,BH2_posn_z = 0.0;\", \"const REAL BH1_mass = 0.5,BH2_mass = 0.5;\"], \n",
" EnableMomentum = False)\n",
"\n",
"dictID['UIUC'] = IDmod_retfunc(modulename = \"BSSN.UIUCBlackHole\", functionname = \"UIUCBlackHole\", \n",
" DestGridCoordSystem = \"SinhSpherical\",\n",
" freeparams = [\"const REAL M = 1.0;\", \"const REAL chi = 0.99;\"], \n",
" EnableMomentum = True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='initializenrpy'></a>\n",
"\n",
"# Step 2: Set up the needed NRPy+ infrastructure and declare core gridfunctions \\[Back to [top](#toc)\\]\n",
"$$\\label{initializenrpy}$$\n",
"\n",
"We will import the core modules of NRPy that we will need and specify the main gridfunctions we will need."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Step P1: Import needed NRPy+ core modules:\n",
"import sys\n",
"sys.path.append(\"nrpytutorial\")\n",
"from outputC import * # NRPy+: Core C code output module\n",
"import finite_difference as fin # NRPy+: Finite difference C code generation module\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 cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface\n",
"import shutil, os, time # Standard Python modules for multiplatform OS-level functions, benchmarking\n",
"import importlib # Standard Python module for interactive module imports\n",
"\n",
"# Step P2: Create C code output directory:\n",
"Ccodesdir = os.path.join(\"BlackHoleID_Ccodes/\")\n",
"# First remove C code output directory if it exists\n",
"# Courtesy https://stackoverflow.com/questions/303200/how-do-i-remove-delete-a-folder-that-is-not-empty\n",
"# !rm -r ScalarWaveCurvilinear_Playground_Ccodes\n",
"shutil.rmtree(Ccodesdir, ignore_errors=True)\n",
"# Then create a fresh directory\n",
"cmd.mkdir(Ccodesdir)\n",
"\n",
"# Step P3: Create executable output directory:\n",
"outdir = os.path.join(Ccodesdir,\"output/\")\n",
"cmd.mkdir(outdir)\n",
"\n",
"# Step 1: Set the spatial dimension parameter \n",
"# to three this time, and then read\n",
"# the parameter as DIM.\n",
"par.set_parval_from_str(\"grid::DIM\",3)\n",
"DIM = par.parval_from_str(\"grid::DIM\")\n",
"\n",
"# Step 2: Set some core parameters, including CoordSystem MoL timestepping algorithm,\n",
"# FD order, floating point precision, and CFL factor:\n",
"# Choices are: Spherical, SinhSpherical, SinhSphericalv2, Cylindrical, SinhCylindrical, \n",
"# SymTP, SinhSymTP\n",
"CoordSystem = \"Spherical\"\n",
"\n",
"# Step 2.a: Set defaults for Coordinate system parameters.\n",
"# These are perhaps the most commonly adjusted parameters,\n",
"# so we enable modifications at this high level.\n",
"\n",
"# domain_size sets the default value for:\n",
"# * Spherical's params.RMAX\n",
"# * SinhSpherical*'s params.AMAX\n",
"# * Cartesians*'s -params.{x,y,z}min & .{x,y,z}max\n",
"# * Cylindrical's -params.ZMIN & .{Z,RHO}MAX\n",
"# * SinhCylindrical's params.AMPL{RHO,Z}\n",
"# * *SymTP's params.AMAX\n",
"domain_size = 3.0\n",
"\n",
"# sinh_width sets the default value for:\n",
"# * SinhSpherical's params.SINHW\n",
"# * SinhCylindrical's params.SINHW{RHO,Z}\n",
"# * SinhSymTP's params.SINHWAA\n",
"sinh_width = 0.4 # If Sinh* coordinates chosen\n",
"\n",
"# sinhv2_const_dr sets the default value for:\n",
"# * SinhSphericalv2's params.const_dr\n",
"# * SinhCylindricalv2's params.const_d{rho,z}\n",
"sinhv2_const_dr = 0.05# If Sinh*v2 coordinates chosen\n",
"\n",
"# SymTP_bScale sets the default value for:\n",
"# * SinhSymTP's params.bScale\n",
"SymTP_bScale = 0.5 # If SymTP chosen\n",
"\n",
"FD_order = 4 # Finite difference order: even numbers only, starting with 2. 12 is generally unstable\n",
"REAL = \"double\" # Best to use double here.\n",
"\n",
"# Step 3: Set the coordinate system for the numerical grid\n",
"par.set_parval_from_str(\"reference_metric::CoordSystem\",CoordSystem)\n",
"rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.\n",
"\n",
"# Step 4: Set the finite differencing order to FD_order (set above).\n",
"par.set_parval_from_str(\"finite_difference::FD_CENTDERIVS_ORDER\", FD_order)\n",
"\n",
"# Step 5: Set the direction=2 (phi) axis to be the symmetry axis; i.e., \n",
"# axis \"2\", corresponding to the i2 direction. \n",
"# This sets all spatial derivatives in the phi direction to zero.\n",
"par.set_parval_from_str(\"indexedexp::symmetry_axes\",\"2\")\n",
"\n",
"# Step 6: The MoLtimestepping interface is only used for memory allocation/deallocation\n",
"import MoLtimestepping.C_Code_Generation as MoL\n",
"from MoLtimestepping.RK_Butcher_Table_Dictionary import Butcher_dict\n",
"RK_method = \"Euler\" # DOES NOT MATTER; Again MoL interface is only used for memory alloc/dealloc.\n",
"RK_order = Butcher_dict[RK_method][1]\n",
"cmd.mkdir(os.path.join(Ccodesdir,\"MoLtimestepping/\"))\n",
"MoL.MoL_C_Code_Generation(RK_method, RHS_string = \"\", post_RHS_string = \"\",\n",
" outdir = os.path.join(Ccodesdir,\"MoLtimestepping/\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='adm_id'></a>\n",
"\n",
"# Step 3: Import Black Hole ADM initial data C function from NRPy+ module \\[Back to [top](#toc)\\]\n",
"$$\\label{adm_id}$$"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Import Black Hole initial data\n",
"\n",
"IDmodule = importlib.import_module(dictID[initial_data_string].modulename)\n",
"IDfunc = getattr(IDmodule, dictID[initial_data_string].functionname)\n",
"IDfunc() # Registers ID C function in dictionary, used below to output to file.\n",
"with open(os.path.join(Ccodesdir,\"initial_data.h\"),\"w\") as file:\n",
" file.write(outC_function_dict[\"initial_data\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='cparams_rfm_and_domainsize'></a>\n",
"\n",
"## Step 3.a: Output C codes needed for declaring and setting Cparameters; also set `free_parameters.h` \\[Back to [top](#toc)\\]\n",
"$$\\label{cparams_rfm_and_domainsize}$$\n",
"\n",
"Based on declared NRPy+ Cparameters, first we generate `declare_Cparameters_struct.h`, `set_Cparameters_default.h`, and `set_Cparameters[-SIMD].h`.\n",
"\n",
"Then we output `free_parameters.h`, which sets initial data parameters, as well as grid domain & reference metric parameters, applying `domain_size` and `sinh_width`/`SymTP_bScale` (if applicable) as set above"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Step 3.a.i: Set free_parameters.h\n",
"# Output to $Ccodesdir/free_parameters.h reference metric parameters based on generic\n",
"# domain_size,sinh_width,sinhv2_const_dr,SymTP_bScale,\n",
"# parameters set above. \n",
"rfm.out_default_free_parameters_for_rfm(os.path.join(Ccodesdir,\"free_parameters.h\"),\n",
" domain_size,sinh_width,sinhv2_const_dr,SymTP_bScale)\n",
"\n",
"# Step 3.a.ii: Generate set_Nxx_dxx_invdx_params__and__xx.h:\n",
"rfm.set_Nxx_dxx_invdx_params__and__xx_h(Ccodesdir)\n",
"\n",
"# Step 3.a.iii: Generate xxCart.h, which contains xxCart() for\n",
"# (the mapping from xx->Cartesian) for the chosen\n",
"# CoordSystem:\n",
"rfm.xxCart_h(\"xxCart\",\"./set_Cparameters.h\",os.path.join(Ccodesdir,\"xxCart.h\"))\n",
"\n",
"# Step 3.a.iv: Generate declare_Cparameters_struct.h, set_Cparameters_default.h, and set_Cparameters[-SIMD].h\n",
"par.generate_Cparameters_Ccodes(os.path.join(Ccodesdir))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='validate'></a>\n",
"\n",
"# Step 4: Validating that the black hole initial data satisfy the Hamiltonian constraint \\[Back to [top](#toc)\\]\n",
"$$\\label{validate}$$\n",
"\n",
"We will validate that the black hole initial data satisfy the Hamiltonian constraint, modulo numerical finite differencing error."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='ham_const_output'></a>\n",
"\n",
"## Step 4.a: Output C code for evaluating the Hamiltonian and Momentum constraint violation \\[Back to [top](#toc)\\]\n",
"$$\\label{ham_const_output}$$\n",
"\n",
"First output C code for evaluating the Hamiltonian constraint violation. For the initial data where `EnableMomentum = True` we must also output C code for evaluating the Momentum constraint violation."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Output C function Hamiltonian_constraint() to file BlackHoleID_Ccodes/Hamiltonian_constraint.h\n",
"Output C function momentum_constraint() to file BlackHoleID_Ccodes/momentum_constraint.h\n"
]
}
],
"source": [
"import BSSN.BSSN_constraints as bssncon\n",
"# Now register the Hamiltonian & momentum constraints as gridfunctions.\n",
"H = gri.register_gridfunctions(\"AUX\",\"H\")\n",
"MU = ixp.register_gridfunctions_for_single_rank1(\"AUX\", \"MU\")\n",
"\n",
"# Generate symbolic expressions for Hamiltonian & momentum constraints\n",
"import BSSN.BSSN_constraints as bssncon\n",
"bssncon.BSSN_constraints(add_T4UUmunu_source_terms=False)\n",
"\n",
"# Generate otpimized C code for Hamiltonian constraint\n",
"desc=\"Evaluate the Hamiltonian constraint\"\n",
"name=\"Hamiltonian_constraint\"\n",
"outCfunction(\n",
" outfile = os.path.join(Ccodesdir,name+\".h\"), desc=desc, name=name,\n",
" params = \"\"\"const paramstruct *restrict params, REAL *restrict xx[3],\n",
" REAL *restrict in_gfs, REAL *restrict aux_gfs\"\"\",\n",
" body = fin.FD_outputC(\"returnstring\",lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"H\"), rhs=bssncon.H),\n",
" params=\"outCverbose=False\").replace(\"IDX4\",\"IDX4S\"),\n",
" loopopts = \"InteriorPoints,Read_xxs\")\n",
"\n",
"# Generate otpimized C code for momentum constraint\n",
"desc=\"Evaluate the momentum constraint\"\n",
"name=\"momentum_constraint\"\n",
"outCfunction(\n",
" outfile = os.path.join(Ccodesdir,name+\".h\"), desc=desc, name=name,\n",
" params = \"\"\"const paramstruct *restrict params, REAL *restrict xx[3],\n",
" REAL *restrict in_gfs, REAL *restrict aux_gfs\"\"\",\n",
" body = fin.FD_outputC(\"returnstring\",\n",
" [lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU0\"), rhs=bssncon.MU[0]),\n",
" lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU1\"), rhs=bssncon.MU[1]),\n",
" lhrh(lhs=gri.gfaccess(\"aux_gfs\", \"MU2\"), rhs=bssncon.MU[2])],\n",
" params=\"outCverbose=False\").replace(\"IDX4\",\"IDX4S\"),\n",
" loopopts = \"InteriorPoints,Read_xxs\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='enforce3metric'></a>\n",
"\n",
"## Step 4.b: Enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint \\[Back to [top](#toc)\\]\n",
"$$\\label{enforce3metric}$$\n",
"\n",
"Then enforce conformal 3-metric $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint (Eq. 53 of [Ruchlin, Etienne, and Baumgarte (2018)](https://arxiv.org/abs/1712.07658)), as [documented in the corresponding NRPy+ tutorial notebook](Tutorial-BSSN-Enforcing_Determinant_gammabar_equals_gammahat_Constraint.ipynb)\n",
"\n",
"Applying curvilinear boundary conditions should affect the initial data at the outer boundary, and will in general cause the $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint to be violated there. Thus after we apply these boundary conditions, we must always call the routine for enforcing the $\\det{\\bar{\\gamma}_{ij}}=\\det{\\hat{\\gamma}_{ij}}$ constraint:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Output C function enforce_detgammabar_constraint() to file BlackHoleID_Ccodes/enforce_detgammabar_constraint.h\n"
]
}
],
"source": [
"# Set up the C function for the det(gammahat) = det(gammabar)\n",
"import BSSN.Enforce_Detgammabar_Constraint as EGC\n",
"enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammabar_Constraint_symb_expressions()\n",
"\n",
"EGC.output_Enforce_Detgammabar_Constraint_Ccode(Ccodesdir,exprs=enforce_detg_constraint_symb_expressions,\n",
" Read_xxs=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='bc_functs'></a>\n",
"\n",
"## Step 4.c: Set up boundary condition functions for chosen singular, curvilinear coordinate system \\[Back to [top](#toc)\\]\n",
"$$\\label{bc_functs}$$\n",
"\n",
"Next apply singular, curvilinear coordinate boundary conditions [as documented in the corresponding NRPy+ tutorial notebook](Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wrote to file \"BlackHoleID_Ccodes/boundary_conditions/parity_conditions_symbolic_dot_products.h\"\n",
"Evolved gridfunction \"aDD00\" has parity type 4.\n",
"Evolved gridfunction \"aDD01\" has parity type 5.\n",
"Evolved gridfunction \"aDD02\" has parity type 6.\n",
"Evolved gridfunction \"aDD11\" has parity type 7.\n",
"Evolved gridfunction \"aDD12\" has parity type 8.\n",
"Evolved gridfunction \"aDD22\" has parity type 9.\n",
"Evolved gridfunction \"alpha\" has parity type 0.\n",
"Evolved gridfunction \"betU0\" has parity type 1.\n",
"Evolved gridfunction \"betU1\" has parity type 2.\n",
"Evolved gridfunction \"betU2\" has parity type 3.\n",
"Evolved gridfunction \"cf\" has parity type 0.\n",
"Evolved gridfunction \"hDD00\" has parity type 4.\n",
"Evolved gridfunction \"hDD01\" has parity type 5.\n",
"Evolved gridfunction \"hDD02\" has parity type 6.\n",
"Evolved gridfunction \"hDD11\" has parity type 7.\n",
"Evolved gridfunction \"hDD12\" has parity type 8.\n",
"Evolved gridfunction \"hDD22\" has parity type 9.\n",
"Evolved gridfunction \"lambdaU0\" has parity type 1.\n",
"Evolved gridfunction \"lambdaU1\" has parity type 2.\n",
"Evolved gridfunction \"lambdaU2\" has parity type 3.\n",
"Evolved gridfunction \"trK\" has parity type 0.\n",
"Evolved gridfunction \"vetU0\" has parity type 1.\n",
"Evolved gridfunction \"vetU1\" has parity type 2.\n",
"Evolved gridfunction \"vetU2\" has parity type 3.\n",
"Auxiliary gridfunction \"H\" has parity type 0.\n",
"Auxiliary gridfunction \"MU0\" has parity type 1.\n",
"Auxiliary gridfunction \"MU1\" has parity type 2.\n",
"Auxiliary gridfunction \"MU2\" has parity type 3.\n",
"Wrote to file \"BlackHoleID_Ccodes/boundary_conditions/EigenCoord_Cart_to_xx.h\"\n"
]
}
],
"source": [
"import CurviBoundaryConditions.CurviBoundaryConditions as cbcs\n",
"cbcs.Set_up_CurviBoundaryConditions(os.path.join(Ccodesdir,\"boundary_conditions/\"),Cparamspath=os.path.join(\"../\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='mainc'></a>\n",
"\n",
"# Step 5: `Initial_Data_Playground.c`: The Main C Code \\[Back to [top](#toc)\\]\n",
"$$\\label{mainc}$$"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Part P0: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER\n",
"# set REAL=double, so that all floating point numbers are stored to at least ~16 significant digits.\n",
"\n",
"with open(os.path.join(Ccodesdir,\"Initial_Data_Playground_REAL__NGHOSTS.h\"), \"w\") as file:\n",
" file.write(\"\"\"\n",
"// Part P0.a: Set the number of ghost cells, from NRPy+'s FD_CENTDERIVS_ORDER\n",
"#define NGHOSTS \"\"\"+str(int(par.parval_from_str(\"finite_difference::FD_CENTDERIVS_ORDER\")/2)+1)+\"\"\"\\n\n",
"// Part P0.b: Set the numerical precision (REAL) to double, ensuring all floating point\n",
"// numbers are stored to at least ~16 significant digits\n",
"#define REAL double\\n\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Writing BlackHoleID_Ccodes//Initial_Data_Playground.c\n"
]
}
],
"source": [
"%%writefile $Ccodesdir/Initial_Data_Playground.c\n",
"\n",
"// Step P0: Define REAL and NGHOSTS. This header is generated by NRPy+.\n",
"#include \"Initial_Data_Playground_REAL__NGHOSTS.h\"\n",
"\n",
"#include \"declare_Cparameters_struct.h\"\n",
"\n",
"// Step P1: Import needed header files\n",
"#include \"stdio.h\"\n",
"#include \"stdlib.h\"\n",
"#include \"math.h\"\n",
"#ifndef M_PI\n",
"#define M_PI 3.141592653589793238462643383279502884L\n",
"#endif\n",
"#ifndef M_SQRT1_2\n",
"#define M_SQRT1_2 0.707106781186547524400844362104849039L\n",
"#endif\n",
"\n",
"// Step P2: Declare the IDX4S(gf,i,j,k) macro, which enables us to store 4-dimensions of\n",
"// data in a 1D array. In this case, consecutive values of \"i\" \n",
"// (all other indices held to a fixed value) are consecutive in memory, where \n",
"// consecutive values of \"j\" (fixing all other indices) are separated by \n",
"// Nxx_plus_2NGHOSTS0 elements in memory. Similarly, consecutive values of\n",
"// \"k\" are separated by Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1 in memory, etc.\n",
"#define IDX4S(g,i,j,k) \\\n",
"( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) + Nxx_plus_2NGHOSTS2 * (g) ) ) )\n",
"#define IDX4ptS(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2) * (g) )\n",
"#define IDX3S(i,j,k) ( (i) + Nxx_plus_2NGHOSTS0 * ( (j) + Nxx_plus_2NGHOSTS1 * ( (k) ) ) )\n",
"#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) \\\n",
" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)\n",
"#define LOOP_ALL_GFS_GPS(ii) _Pragma(\"omp parallel for\") \\\n",
" for(int (ii)=0;(ii)<Nxx_plus_2NGHOSTS_tot*NUM_EVOL_GFS;(ii)++)\n",
"\n",
"// Step P3: Set UUGF and VVGF macros, as well as xxCart()\n",
"#include \"boundary_conditions/gridfunction_defines.h\"\n",
"\n",
"// Step P4: Set xxCart(const paramstruct *restrict params, \n",
"// REAL *restrict xx[3],\n",
"// const int i0,const int i1,const int i2, \n",
"// REAL xCart[3]),\n",
"// which maps xx->Cartesian via\n",
"// {xx[0][i0],xx[1][i1],xx[2][i2]}->{xCart[0],xCart[1],xCart[2]}\n",
"#include \"xxCart.h\"\n",
"\n",
"// Step P5: Defines set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3], \n",
"// paramstruct *restrict params, REAL *restrict xx[3]),\n",
"// which sets params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for\n",
"// the chosen Eigen-CoordSystem if EigenCoord==1, or\n",
"// CoordSystem if EigenCoord==0.\n",
"#include \"set_Nxx_dxx_invdx_params__and__xx.h\"\n",
"\n",
"// Step P6: Include basic functions needed to impose curvilinear\n",
"// parity and boundary conditions.\n",
"#include \"boundary_conditions/CurviBC_include_Cfunctions.h\"\n",
"\n",
"// Step P8: Include function for enforcing detgammabar constraint.\n",
"#include \"enforce_detgammabar_constraint.h\"\n",
"\n",
"// Step P10: Declare function necessary for setting up the initial data.\n",
"// Step P10.a: Define BSSN_ID() for BrillLindquist initial data\n",
"\n",
"// Step P10.b: Set the generic driver function for setting up BSSN initial data\n",
"#include \"initial_data.h\"\n",
"\n",
"// Step P11: Declare function for evaluating Hamiltonian constraint (diagnostic)\n",
"#include \"Hamiltonian_constraint.h\" \n",
"#include \"momentum_constraint.h\" \n",
"\n",
"// main() function:\n",
"// Step 0: Read command-line input, set up grid structure, allocate memory for gridfunctions, set up coordinates\n",
"// Step 1: Set up initial data to an exact solution\n",
"// Step 2: Start the timer, for keeping track of how fast the simulation is progressing.\n",
"// Step 3: Integrate the initial data forward in time using the chosen RK-like Method of \n",
"// Lines timestepping algorithm, and output periodic simulation diagnostics \n",
"// Step 3.a: Output 2D data file periodically, for visualization\n",
"// Step 3.b: Step forward one timestep (t -> t+dt) in time using \n",
"// chosen RK-like MoL timestepping algorithm\n",
"// Step 3.c: If t=t_final, output conformal factor & Hamiltonian \n",
"// constraint violation to 2D data file\n",
"// Step 3.d: Progress indicator printing to stderr\n",
"// Step 4: Free all allocated memory\n",
"int main(int argc, const char *argv[]) {\n",
" paramstruct params;\n",
"#include \"set_Cparameters_default.h\"\n",
" \n",
" // Step 0a: Read command-line input, error out if nonconformant\n",
" if((argc != 4) || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < 2 /* FIXME; allow for axisymmetric sims */) {\n",
" fprintf(stderr,\"Error: Expected three command-line arguments: ./BrillLindquist_Playground Nx0 Nx1 Nx2,\\n\");\n",
" fprintf(stderr,\"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.\\n\");\n",
" fprintf(stderr,\"Nx[] MUST BE larger than NGHOSTS (= %d)\\n\",NGHOSTS);\n",
" exit(1);\n",
" }\n",
" // Step 0b: Set up numerical grid structure, first in space...\n",
" const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };\n",
" if(Nxx[0]%2 != 0 || Nxx[1]%2 != 0 || Nxx[2]%2 != 0) {\n",
" fprintf(stderr,\"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.\\n\");\n",
" fprintf(stderr,\" For example, in case of angular directions, proper symmetry zones will not exist.\\n\");\n",
" exit(1);\n",
" }\n",
"\n",
" // Step 0c: Set free parameters, overwriting Cparameters defaults \n",
" // by hand or with command-line input, as desired.\n",
"#include \"free_parameters.h\"\n",
"\n",
" // Step 0d: Uniform coordinate grids are stored to *xx[3]\n",
" REAL *xx[3];\n",
" // Step 0d.i: Set bcstruct\n",
" bc_struct bcstruct;\n",
" {\n",
" int EigenCoord = 1;\n",
" // Step 0d.ii: Call set_Nxx_dxx_invdx_params__and__xx(), which sets\n",
" // params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the\n",
" // chosen Eigen-CoordSystem.\n",
" set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);\n",
" // Step 0d.iii: Set Nxx_plus_2NGHOSTS_tot\n",
"#include \"set_Cparameters-nopointer.h\"\n",
" const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;\n",
" // Step 0e: Find ghostzone mappings; set up bcstruct\n",
"#include \"boundary_conditions/driver_bcstruct.h\"\n",
" // Step 0e.i: Free allocated space for xx[][] array\n",
" for(int i=0;i<3;i++) free(xx[i]);\n",
" }\n",
" \n",
" // Step 0f: Call set_Nxx_dxx_invdx_params__and__xx(), which sets\n",
" // params Nxx,Nxx_plus_2NGHOSTS,dxx,invdx, and xx[] for the\n",
" // chosen (non-Eigen) CoordSystem.\n",
" int EigenCoord = 0;\n",
" set_Nxx_dxx_invdx_params__and__xx(EigenCoord, Nxx, ¶ms, xx);\n",
"\n",
" // Step 0g: Set all C parameters \"blah\" for params.blah, including\n",
" // Nxx_plus_2NGHOSTS0 = params.Nxx_plus_2NGHOSTS0, etc.\n",
"#include \"set_Cparameters-nopointer.h\"\n",
" const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;\n",
"\n",
" // Step 0j: Error out if the number of auxiliary gridfunctions outnumber evolved gridfunctions.\n",
" // This is a limitation of the RK method. You are always welcome to declare & allocate \n",
" // additional gridfunctions by hand.\n",
" if(NUM_AUX_GFS > NUM_EVOL_GFS) {\n",
" fprintf(stderr,\"Error: NUM_AUX_GFS > NUM_EVOL_GFS. Either reduce the number of auxiliary gridfunctions,\\n\");\n",
" fprintf(stderr,\" or allocate (malloc) by hand storage for *diagnostic_output_gfs. \\n\");\n",
" exit(1);\n",
" }\n",
" \n",
" // Step 0k: Allocate memory for gridfunctions\n",
"#include \"MoLtimestepping/RK_Allocate_Memory.h\"\n",
" REAL *restrict auxevol_gfs = (REAL *)malloc(sizeof(REAL) * NUM_AUXEVOL_GFS * Nxx_plus_2NGHOSTS_tot);\n",
" \n",
" // Step 1: Set up initial data to an exact solution\n",
" initial_data(¶ms, xx, y_n_gfs);\n",
"\n",
" // Step 1b: Apply boundary conditions, as initial data \n",
" // are sometimes ill-defined in ghost zones.\n",
" // E.g., spherical initial data might not be\n",
" // properly defined at points where r=-1.\n",
" apply_bcs_curvilinear(¶ms, &bcstruct, NUM_EVOL_GFS,evol_gf_parity, y_n_gfs);\n",
" enforce_detgammabar_constraint(¶ms, xx, y_n_gfs);\n",
"\n",
" // Evaluate Hamiltonian & momentum constraint violations\n",
" Hamiltonian_constraint(¶ms, xx, y_n_gfs, diagnostic_output_gfs);\n",
" momentum_constraint( ¶ms, xx, y_n_gfs, diagnostic_output_gfs);\n",
"\n",
" /* Step 2: 2D output: Output conformal factor (CFGF) and constraint violations (HGF, MU0GF, MU1GF, MU2GF). */\n",
" const int i0MIN=NGHOSTS; // In spherical, r=Delta r/2.\n",
" const int i1mid=Nxx_plus_2NGHOSTS1/2;\n",
" const int i2mid=Nxx_plus_2NGHOSTS2/2;\n",
" LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS0-NGHOSTS, i1mid,i1mid+1, NGHOSTS,Nxx_plus_2NGHOSTS2-NGHOSTS) {\n",
" REAL xCart[3];\n",
" xxCart(¶ms, xx, i0,i1,i2, xCart);\n",
" int idx = IDX3S(i0,i1,i2);\n",
" printf(\"%e %e %e %e %e %e %e\\n\",xCart[0],xCart[1], y_n_gfs[IDX4ptS(CFGF,idx)],\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(HGF,idx)])),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU0GF,idx)])+1e-200),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU1GF,idx)])+1e-200),\n",
" log10(fabs(diagnostic_output_gfs[IDX4ptS(MU2GF,idx)])+1e-200));\n",
" }\n",
" // Step 4: Free all allocated memory\n",
"#include \"boundary_conditions/bcstruct_freemem.h\"\n",
"#include \"MoLtimestepping/RK_Free_Memory.h\"\n",
" free(auxevol_gfs);\n",
" for(int i=0;i<3;i++) free(xx[i]);\n",
"\n",
" return 0;\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Compiling executable...\n",
"Executing `gcc -Ofast -fopenmp -march=native -funroll-loops BlackHoleID_Ccodes/Initial_Data_Playground.c -o Initial_Data_Playground -lm`...\n",
"Finished executing in 2.815635919570923 seconds.\n",
"Finished compilation.\n",
"Executing `taskset -c 0,1,2,3 ./Initial_Data_Playground 96 96 96`...\n",
"Finished executing in 0.41163206100463867 seconds.\n",
"Executing `taskset -c 0,1,2,3 ./Initial_Data_Playground 48 48 48`...\n",
"Finished executing in 0.2117936611175537 seconds.\n"
]
}
],
"source": [
"import cmdline_helper as cmd\n",
"\n",
"cmd.C_compile(os.path.join(Ccodesdir,\"Initial_Data_Playground.c\"), \"Initial_Data_Playground\")\n",
"cmd.delete_existing_files(\"out*.txt\")\n",
"cmd.delete_existing_files(\"out*.png\")\n",
"args_output_list = [[\"96 96 96\", \"out96.txt\"], [\"48 48 48\", \"out48.txt\"]]\n",
"for args_output in args_output_list:\n",
" cmd.Execute(\"Initial_Data_Playground\", args_output[0], args_output[1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='plot'></a>\n",
"\n",
"# Step 6: Plotting the initial data \\[Back to [top](#toc)\\]\n",
"$$\\label{plot}$$\n",
"\n",
"Here we plot the evolved conformal factor of these initial data on a 2D grid, such that darker colors imply stronger gravitational fields. Hence, we see the black hole(s) centered at $x/M=\\pm 1$, where $M$ is an arbitrary mass scale (conventionally the [ADM mass](https://en.wikipedia.org/w/index.php?title=ADM_formalism&oldid=846335453) is chosen), and our formulation of Einstein's equations adopt $G=c=1$ [geometrized units](https://en.wikipedia.org/w/index.php?title=Geometrized_unit_system&oldid=861682626)."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3de5BcdZ3//1d3zy0hF3KRm0xIDKIgRhdIYSJgsq6RgIasJauCXCSwhQK1iOUq4GLiilFBwAuweEusLV1AWSQiUMHlEi9gCN+grCwoIAYNKjGQQCBz6e7fH/Nj5vN5n+nPmTPT3WdmPs9HVarSOafP+XT36ebT/Xnxfheq1WpVAAAAiEYx7wEAAACguZgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkgAABAZJgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkgAABAZJgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkgAABAZJgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkgAABAZJgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkgAABAZJgAAgAARIYJIAAAQGSYAAIAAESGCSAAAEBkmAACAABEhgkggKZYu3atCoWCNm3aNKz7FwoFrVy5sv/2I488opUrV+qpp55K7Hv66adr9uzZwzrPUO97+umnq1Ao9P/ZY489NHv2bC1btkxr1qxRV1fXsM4vSbfddpv3WAGg3pgAAhgT7rvvPp155pn9tx955BGtWrVq0Angv/3bv+nmm29u+JgmTJig++67T/fdd59uvfVWfeYzn9Eee+yhs846S4cffrj++Mc/Duu4t912m1atWlXn0QLAgJa8BwAAQ/GWt7xlyPvOnTu3gSMZUCwWE+M69dRT9aEPfUjvete79N73vlf3339/U8YCAFnwCyCA3Jx++umaNGmSHn/8cR133HGaNGmSOjs79bGPfSyxhOouAa9du1YnnniiJGnx4sX9y7Br167tP65dxr366qt1zDHHaK+99tIee+yhN77xjfriF7+onp6euj+uJUuW6KyzztIvf/lLbdiwof/fb7jhBi1ZskT77ruvJkyYoIMPPlif/OQntWvXLu85ufrqq/sf8yt/Xvmls5mPA8D4xS+AAHLV09OjZcuWacWKFfrYxz6mDRs26N///d81depUXXLJJYPe5/jjj9fnPvc5XXTRRbr66qt12GGHSQr/8vfEE0/opJNO0pw5c9TW1qZf/epXuvTSS/Xoo4/q29/+dt0f17Jly3TNNddow4YNOuaYYyRJv/vd73Tcccfp/PPP1x577KFHH31UX/jCF7Rx40bdddddkvqWr3ft2qUf/OAHuu+++/qPt+++++byOACMT0wAAeSqu7tbq1at6v9F7+1vf7s2bdqk733vezUngK961av02te+VpJ0yCGHDGl5+Iorruj/e6VS0dFHH60ZM2boQx/6kL70pS9p2rRpdXg0Aw444ABJ0tatW/v/7VOf+lT/36vVqt761rfq4IMP1tve9jb9+te/1rx58zR37lztvffekgZf9m724wAwPrEEDCBXhUJB7373u71/mzdvnv7whz/U9TybN2/WsmXLNGPGDJVKJbW2turUU09VuVzWb3/727qeS+qb4FlPPvmkTjrpJO2zzz79Y3jb294mSfq///u/IR232Y8DwPjEL4AAcjVx4kR1dHR4/9be3q7du3fX7RxbtmzR0Ucfrde97nX68pe/rNmzZ6ujo0MbN27UOeeco5dffrlu53rFKxPY/fbbT5L04osv6uijj1ZHR4c++9nP6qCDDtLEiRP19NNP6z3vec+QxpDH4wAwPjEBBDDu/fCHP9SuXbv03//93/1Ls5L00EMPNeyc69atkyQtWrRIknTXXXdp69atuueee/p/9ZOk559/fsjHzONxABifWAIGMCa1t7dL0pB+9SoUCt59pL4l2m984xsNGdudd96pb37zm1q4cKGOOuqommOQpOuuuy5x/1qPrdmPA8D4xS+AAMakQw89VJL09a9/XZMnT1ZHR4fmzJmjGTNmJPZ9xzveoba2Nn3gAx/Qv/7rv2r37t269tpr9dxzz41oDJVKpb/OX1dXl7Zs2aLbb79dN954ow4++GDdeOON/fsuXLhQ06ZN09lnn61Pf/rTam1t1Xe/+1396le/Shz3jW98oyTpC1/4gpYuXapSqaR58+Y17HEAiA+/AAIYk+bMmaOrrrpKv/rVr7Ro0SLNnz9fP/rRjwbd9/Wvf71uuukmPffcc3rPe96j8847T29+85v1la98ZURjePnll7VgwQItWLBAS5cu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NWwU9/1fMl/QS4Wx/diAV4yv/8qNIbfddpuefPJJHXnkkfr4xz+u/fffX5/4xCf02GOP5T00AAAwzjEBzNG+++6rCy+8UI899phuuOEG/fWvf9X8+fP11re+VS+/HP4lAgAAYLhYAh4l5s+fr6eeekqPPPKINm/erJ6eHk2YMKHh573mmmt02WWX6ZlnntEb3vAGXXXVVTr66KMbft6xZqyXjMmiXBl73wvdMjCJVm+mvZst9eIu+5Z/+0TwPKWD5g4cx2xrMWVhWpxWcWWzHFzxV49HJXsdtBRHX9u1ekmWdgHGP67znN13330666yztM8+++irX/2qTjvtNG3dulVTpkxp+LlvuOEGnX/++br44ou1efNmHX300Vq6dKm2bNnS8HMDAID8MAHMyRe/+EUdfPDBOuGEEzRp0iT97Gc/0wMPPKCPfOQj2nPPPZsyhiuuuEIrVqzQmWeeqYMPPlhXXXWVOjs7de211zbl/AAAIB8sAefkk5/8pGbNmqV/+qd/UqFQ0Jo1awbd74orrmjI+bu7u/Xggw/qk5/8pPfvS5Ys0S9+8YvE/l1dXerq6uq/vXPnzoaMCwAANB4TwJwcc8wxKhQK+s1vflNzn0Khcbmzbdu2qVwua++99/b+fe+999af//znxP6rV6/WqlWrGjYeAADQPEwAc3LPPffkPQRJyUlmtVoddOJ54YUX6oILLui/vXPnTnV2djZ8fAAAoP6YAOZk4cKFWr58uZYtW6bXv/71TT//zJkzVSqVEr/2/fWvf038KihJ7e3tam9vb9bwAABAA/E/geTkn//5n/XLX/5S8+fP10EHHaSPf/zj+ulPf1qzLVy9tbW16fDDD9edd97p/fudd96phQsXNmUMAAAgH/wCmJPTTz9dp59+urq6uvSTn/xE69at0/ve9z719PTo+OOP1wknnKB3vvOdmjhxYsPGcMEFF+iUU07REUccoQULFujrX/+6tmzZorPPPrth5xyrSvIn5uO5GVTJ1nur5N8mLo1bV8+2YOu1LdommxZuzt/dOn+DnmfKQG1Oe5zEeZxxjIW6f1biOhjH7PsbiAG/AOasvb1dxx9/vK677jpt3bpVt956q1796lfrkksu0cyZM/Wud71LP//5zxty7ve973266qqr9JnPfEZvfvObtWHDBt1222064IADGnI+AAAwOjABzNnpp5+u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O3bskCTtfLEy6P4AgNHtlc9vfgeKExPAiH3iE5/Q1772Nb300kt6y1veoltvvbXmvqtXr9aqVasS/37AYU81cIQAgEb729/+pqlTp+Y9DDQZS8DjyMqVKwedpLkeeOABHXHEEZKkbdu2afv27frDH/6gVatWaerUqbr11ltVKCR7sNpfAJ9//nkdcMAB2rJlS1QfHDt37lRnZ6eefvrpaJZMYnzMEo87pscd42OW+lZyZs2apeeee0577rln3sNBkzEBHEe2bdumbdu2BfeZPXu2Ojo6Ev/+xz/+UZ2dnfrFL36hBQsWpJ7rlQxgbNmRGB93jI9Z4nHH9LhjfMxSvI8bfVgCHkdmzpypmTNnDuu+r3wPcH/lAwAA4xMTwAht3LhRGzdu1FFHHaVp06bpySef1CWXXKK5c+cO6dc/AAAwtpVWrly5Mu9BoLm2b9+uK6+8Ul/84hd1xRVX6K677tLChQu1Zs0aTZs2bcjHKZVKWrRokVpa4voeEePjjvExSzzumB53jI9ZivdxgwwgAABAdKjgCwAAEBkmgAAAAJFhAggAABAZJoAAAACRYQKIuli2bJlmzZqljo4O7bvvvjrllFO0devWvIfVME899ZRWrFihOXPmaMKECZo7d64+/elPq7u7O++hNdyll16qhQsXauLEieO2e8A111yjOXPmqKOjQ4cffrh++tOf5j2khtuwYYPe/e53a7/99lOhUNAPf/jDvIfUcKtXr9b8+fM1efJk7bXXXlq+fLkee+yxvIfVUNdee63mzZunKVOmaMqUKVqwYIFuv/32vIeFHDABRF0sXrxYN954ox577DHddNNNeuKJJ/Te974372E1zKOPPqpKpaLrrrtOv/nNb3TllVfqP/7jP3TRRRflPbSG6+7u1oknnqgPf/jDeQ+lIW644Qadf/75uvjii7V582YdffTRWrp0qbZs2ZL30Bpq165detOb3qSvfe1reQ+lae69916dc845uv/++3XnnXeqt7dXS5Ys0a5du/IeWsPsv//++vznP69NmzZp06ZN+vu//3udcMIJ+s1vfpP30NBklIFBQ6xbt07Lly9XV1eXWltb8x5OU1x22WW69tpr9eSTT+Y9lKZYu3atzj//fD3//PN5D6WujjzySB122GG69tpr+//t4IMP1vLly7V69eocR9Y8hUJBN998s5YvX573UJrq2Wef1V577aV7771XxxxzTN7DaZrp06frsssu04oVK/IeCpqIXwBRd9u3b9d3v/tdLVy4MJrJn9TXWH369Ol5DwMj0N3drQcffFBLlizx/n3JkiX6xS9+kdOo0Cw7duyQpGjex+VyWddff7127dpFF6gIMQFE3XziE5/QHnvsoRkzZmjLli265ZZb8h5S0zzxxBP66le/qrPPPjvvoWAEtm3bpnK5rL333tv797333lt//vOfcxoVmqFareqCCy7QUUcdpUMPPTTv4TTUww8/rEmTJqm9vV1nn322br75Zh1yyCF5DwtNxgQQNa1cuVKFQiH4Z9OmTf37f/zjH9fmzZu1fv16lUolnXrqqRprCYOsj1mStm7dqmOPPVYnnniizjzzzJxGPjLDedzjWaFQ8G5Xq9XEv2F8Offcc/XrX/9a//Vf/5X3UBruda97nR566CHdf//9+vCHP6zTTjtNjzzySN7DQpPR/A81nXvuuXr/+98f3Gf27Nn9f585c6Zmzpypgw46SAcffLA6Ozt1//33j6mlhayPeevWrVq8eLEWLFigr3/96w0eXeNkfdzj1cyZM1UqlRK/9v31r39N/CqI8eO8887TunXrtGHDBu2///55D6fh2tradOCBB0qSjjjiCD3wwAP68pe/rOuuuy7nkaGZmACiplcmdMPxyi9/XV1d9RxSw2V5zH/605+0ePFiHX744VqzZo2KxbH7g/pIXuvxpK2tTYcffrjuvPNO/eM//mP/v99555064YQTchwZGqFareq8887TzTffrHvuuUdz5szJe0i5qFarY+6zGiPHBBAjtnHjRm3cuFFHHXWUpk2bpieffFKXXHKJ5s6dO6Z+/cti69atWrRokWbNmqXLL79czz77bP+2ffbZJ8eRNd6WLVu0fft2bdmyReVyWQ899JAk6cADD9SkSZNyHt3IXXDBBTrllFN0xBFH9P+yu2XLlnGf73zxxRf1+OOP99/+/e9/r4ceekjTp0/XrFmzchxZ45xzzjn63ve+p1tuuUWTJ0/u/+V36tSpmjBhQs6ja4yLLrpIS5cuVWdnp1544QVdf/31uueee3THHXfkPTQ0WxUYoV//+tfVxYsXV6dPn15tb2+vzp49u3r22WdX//jHP+Y9tIZZs2ZNVdKgf8a70047bdDHfffdd+c9tLq5+uqrqwcccEC1ra2tethhh1XvvffevIfUcHffffegr+tpp52W99AaptZ7eM2aNXkPrWHOOOOM/mv7Va96VfXtb397df369XkPCzmgDiAAAEBkxm5oCQAAAMPCBBAAACAyTAABAAAiwwQQAAAgMkwAAQAAIsMEEAAAIDJMAAEAACLDBBAAACAyTAABAAAiwwQQAIy1a9fqLW95iyRp0aJFKhQK+vznP5/Y77jjjlOhUNDKlSubPEIAGBkmgABgrFu3TieccEL/7c7OTq1Zs8bbZ+vWrbrrrru07777Nnt4ADBiTAABROPZZ5/VPvvso8997nP9//bLX/5SbW1tWr9+vSRp9+7dWr9+vZYtW9a/z7ve9S797W9/089//vP+f1u7dq2WLFmivfbaq3kPAADqhAkggGi86lWv0re//W2tXLlSmzZt0osvvqgPfvCD+shHPqIlS5ZIkv7nf/5H++yzj97whjf036+trU0nn3yy9yvg2rVrdcYZZzT9MQBAPTABBBCV4447TmeddZZOPvlknX322ero6PDyfbfccou3/PuKFStW6MYbb9SuXbu0YcMG7dixQ8cff3wzhw4AddOS9wAAoNkuv/xyHXroobrxxhu1adMmdXR0SJKq1ap+9KMf6frrr0/cZ968eXrta1+rH/zgB7r77rt1yimnqLW1tdlDB4C6YAIIIDpPPvmktm7dqkqloj/84Q+aN2+eJGnjxo3q7u7WUUcdNej9zjjjDF199dV65JFHtHHjxmYOGQDqiiVgAFHp7u7WySefrPe973367Gc/qxUrVugvf/mLpL7l3+OPP16lUmnQ+5500kl6+OGHdeihh+qQQw5p5rABoK74BRBAVC6++GLt2LFDX/nKVzRp0iTdfvvtWrFihW699VatW7dOq1atqnnfadOm6ZlnnmHpF8CYxwQQQDTuueceXXXVVbr77rs1ZcoUSdJ//ud/at68ebr66qv1+OOP653vfGfwGHvuuWczhgoADVWoVqvVvAcBAHm74oor9JOf/ES33XZb3kMBgIYjAwgAkvbff39deOGFeQ8DAJqCXwABAAAiwy+AAAAAkWECCAAAEBkmgAAAAJFhAggAABAZJoAAAACRYQIIAAAQGSaAAAAAkWECCAAAEBkmgAAAAJH5/wCuhcPDQ6g5wgAAAABJRU5ErkJggg==\n",
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import numpy as np\n",
"from scipy.interpolate import griddata\n",
"from pylab import savefig\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.cm as cm\n",
"from IPython.display import Image\n",
"\n",
"x96,y96,valuesCF96,valuesHam96,valuesmomr96,valuesmomtheta96,valuesmomphi96 = np.loadtxt('out96.txt').T #Transposed for easier unpacking\n",
"\n",
"\n",
"pl_xmin = -3.\n",
"pl_xmax = +3.\n",
"pl_ymin = -3.\n",
"pl_ymax = +3.\n",
"\n",
"grid_x, grid_y = np.mgrid[pl_xmin:pl_xmax:100j, pl_ymin:pl_ymax:100j]\n",
"points96 = np.zeros((len(x96), 2))\n",
"for i in range(len(x96)):\n",
" points96[i][0] = x96[i]\n",
" points96[i][1] = y96[i]\n",
"\n",
"grid96 = griddata(points96, valuesCF96, (grid_x, grid_y), method='nearest')\n",
"grid96cub = griddata(points96, valuesCF96, (grid_x, grid_y), method='cubic')\n",
"\n",
"plt.clf()\n",
"plt.title(\"Initial Data\")\n",
"plt.xlabel(\"x/M\")\n",
"plt.ylabel(\"y/M\")\n",
"\n",
"# fig, ax = plt.subplots()\n",
"#ax.plot(grid96cub.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))\n",
"plt.imshow(grid96.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))\n",
"savefig(\"ID.png\")\n",
"plt.close()\n",
"Image(\"ID.png\")\n",
"# # interpolation='nearest', cmap=cm.gist_rainbow)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='convergence'></a>\n",
"\n",
"# Step 7: Validation: Convergence of numerical errors (Hamiltonian & momentum constraint violations) to zero \\[Back to [top](#toc)\\]\n",
"$$\\label{convergence}$$\n",
"\n",
"**Special thanks to George Vopal for creating the following plotting script.**\n",
"\n",
"The equations behind these initial data solve Einstein's equations exactly, at a single instant in time. One reflection of this solution is that the Hamiltonian constraint violation should be exactly zero in the initial data. \n",
"\n",
"However, when evaluated on numerical grids, the Hamiltonian constraint violation will *not* generally evaluate to zero due to the associated numerical derivatives not being exact. However, these numerical derivatives (finite difference derivatives in this case) should *converge* to the exact derivatives as the density of numerical sampling points approaches infinity.\n",
"\n",
"In this case, all of our finite difference derivatives agree with the exact solution, with an error term that drops with the uniform gridspacing to the fourth power: $\\left(\\Delta x^i\\right)^4$. \n",
"\n",
"Here, as in the [Start-to-Finish Scalar Wave (Cartesian grids) NRPy+ tutorial](Tutorial-Start_to_Finish-ScalarWave.ipynb) and the [Start-to-Finish Scalar Wave (curvilinear grids) NRPy+ tutorial](Tutorial-Start_to_Finish-ScalarWaveCurvilinear.ipynb) we confirm this convergence.\n",
"\n",
"First, let's take a look at what the numerical error looks like on the x-y plane at a given numerical resolution, plotting $\\log_{10}|H|$, where $H$ is the Hamiltonian constraint violation:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 432x288 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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ABeZXf8dwItFZALJhf7zo24rzVIKoNvt/x8RjS3GGmpdpLGGHbApFmcjmar+GQB/nK77WQAnm32mAHwPwK8DWKSqd5iyViIiohDGGqKaeDNBsdgDwFIAkwAWisgaAHep6s5p+30OwJcBnDna5tGsxzIlormAsYbaFWGs4c0ExWITgNMAHADg/QBuRDntnPkugL8A8E+jaxrNCRFe4ImoNsYaaleEsYY3EzT2ROQNAHap6j+mNar/A+CNAI6cvq+qKoALRtxEoqETkaMBfALAQgC7APyhql7ebquIZg/GGqJmeDNBY09VPwPgM+nylIi8GMB/q+odZp+Xeo5dOoo20myn4zAr6XkAzlLVb4rIz6dfv7TdJhHNHow11L6xiDW18WaCoqOqOwBsbLsdNIcooNr6aEwKYFm6vBzAvS22hWjWY6yhkRuPWFMbbyaIiOLwVgAXi8gFSIb1flHL7SEiIuLNBBFRJYNJPa8UkSvN15tUdVP2hYhcAmCt47hTAbwCwJ+q6r+IyK8B+CSA4wfRKCIiGhMscyIimqUGM8LGNlX1lk2oqvfmQEQ+A+BP0i//GcDfDqJBREQ0RjiaExHRLKQ6DjNY3wvgJQAuBfByAD9qtTVERDRY4xFrauPNBBFRHH4PwIdEZB6S2XlPark9REREvJkgIqqk5dSzqn4PwE+32ggiIhouljkREc1OGmHqmYiI4hJjrOm03QAiIiIiIooTMxNEREEaZeqZiIhiEmes4c0EEVGIIsqxv4mIKCKRxhqWORERERERUSPMTBARVaHxdYojIqLIRBhreDNBRBSgADTC1DMREcUj1ljDmwkiohDVKJ8WERFRRCKNNewzQUREREREjTAzQURUQYypZyIiikuMsYY3E0REVUSYeiYioshEGGtEI5wcg4holETkIgArB3Cqbap6wgDOQ0REs0yssYY3E0RERERE1Ag7YBMRERERUSO8mSAiIiIiokZ4M0FERERERI3wZoKIiIiIiBrhzQQRERERETXCmwkiIiIiImqENxNERERERNQIbyaIiIiIiKgR3kwQEREREVEjvJkgIiIiIqJGeDNBRERERESN8GaCiIiIiIga4c0EERERERE1wpsJIiIiIiJqhDcTRERERETUCG8miIiIiIioEd5MEBERERFRI7yZICIiIiKiRngzQUREREREjfBmgoiIiIiIGuHNBBERERERNcKbCSIiIiIiaoQ3E0RERERE1AhvJoiIiIiIqBHeTBARERERUSO8mSAiIiIiokZ4M0FERERERI3wZoKIiIiIiBrhzQQRERERETXCmwkiIiIiImqENxNERERERNQIbyaIiIiIiKgR3kwQEREREVEjvJkgIiIiIqJGeDNBRERERESN8GaCiIiIiIga4c0EERERERE1wpsJIiIiIiJqhDcTRERERETUCG8miIiIiIioEd5MEBERERFRI7yZICIiIiKiRngzQUREREREjcxruwFEROPu1S9bog8/MtX3ea66dsfFqnrCAJpERESzTKyxhjcTREQBDz8yhcsvXt/3eSb2+tHKATSHiIhmoVhjDW8miIgCFEAX3babQUREs1issYY3E0REQYopje8CT0REMYkz1rADNhERERERNcLMBBFRQJJ61rabQUREs1issYY3E0REFcRYx0pERHGJMdbwZoKIKEChmNL4nhYREVE8RhlrROQEAB8CMAHgb1X1fU3PxT4TRERERERzhIhMAPgogNcAOAzAb4rIYU3Px8wEEVEFMdaxEhFRXEYUa44FcIuq3gYAIvI5ACcCuKHJyXgzQUQUoACmeDNBRERDNMJYsw+Au8zXdwN4QdOT8WaCiIiIiGj2WCkiV5qvN6nqJvO1OI5pfBfDmwkiogpY5kRERMM2oFizTVU3zrD9bgDrzNf7Ari36YvxZoKIKEABjuZERERDNcJYcwWA54rIAQDuAfAbAH6r6cl4M0FERERENEeo6i4ReTOAi5EMDfspVb2+6fl4M0FEVEF80wgREVFsRhVrVPUbAL4xiHPxZoKIKEChHM2JiIiGKtZYw5sJIqIQBabiu74TEVFMIo01nAGbiIiIiIgaYWaCiChAwT4TREQ0XLHGGt5MEBEFCaacc/wQERENSpyxhmVORERERETUCDMTREQBCqAbYac4IiKKR6yxhjcTREQVxJh6JiKiuMQYa1jmREREREREjTAzQUQUoIjzaREREcUj1ljDmwkiogq6Gt8FnoiI4hJjrGGZExERERERNcLMBBFRQKypZyIiikessYY3E5EQkTUAvgzgWQBTAP63qt7XbqtothCRgwF8CMAqACer6qXttmi8KARTTOTSLMc4Q6MgIpeq6kvbbsc4ijXW8GYiHtsA/JyqdkXkdwD8HwDvbbdJNBuISAfAxwD8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\n",
"text/plain": [
"<Figure size 864x864 with 8 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"RefData=[valuesHam96,valuesmomr96,valuesmomtheta96,valuesmomphi96]\n",
"SubTitles=[\"\\mathcal{H}\",'\\mathcal{M}^r',r\"\\mathcal{M}^{\\theta}\",\"\\mathcal{M}^{\\phi}\"]\n",
"axN = [] #this will let us automate the subplots in the loop that follows\n",
"grid96N = [] #we need to calculate the grid96 data for each constraint for use later\n",
"plt.clf()\n",
"\n",
"# We want to create four plots. One for the Hamiltonian, and three for the momentum\n",
"# constraints (r,th,ph)\n",
"# Define the size of the overall figure\n",
"fig = plt.figure(figsize=(12,12)) # 8 in x 8 in\n",
"\n",
"num_plots = 4\n",
"\n",
"if dictID[initial_data_string].EnableMomentum == False:\n",
" num_plots = 1\n",
"\n",
"\n",
"for p in range(num_plots):\n",
" grid96 = griddata(points96, RefData[p], (grid_x, grid_y), method='nearest')\n",
" grid96N.append(grid96)\n",
" grid96cub = griddata(points96, RefData[p], (grid_x, grid_y), method='cubic')\n",
"\n",
" #fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)\n",
"\n",
"\n",
" #Generate the subplot for the each constraint\n",
" ax = fig.add_subplot(221+p)\n",
" axN.append(ax) # Grid of 2x2\n",
" \n",
" axN[p].set_xlabel('x/M')\n",
" axN[p].set_ylabel('y/M')\n",
" axN[p].set_title('$96^3$ Numerical Err.: $log_{10}|'+SubTitles[p]+'|$')\n",
"\n",
" fig96cub = plt.imshow(grid96cub.T, extent=(pl_xmin,pl_xmax, pl_ymin,pl_ymax))\n",
" cb = plt.colorbar(fig96cub)\n",
" \n",
"# Adjust the spacing between plots\n",
"plt.tight_layout(pad=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we set up the same initial data but on a lower-resolution, $48^3$ grid. Since the constraint violation (numerical error associated with the fourth-order-accurate, finite-difference derivatives) should converge to zero with the uniform gridspacing to the fourth power: $\\left(\\Delta x^i\\right)^4$, we expect the constraint violation will increase (relative to the $96^3$ grid) by a factor of $\\left(96/48\\right)^4$. Here we demonstrate that indeed this order of convergence is observed as expected. I.e., at all points *except* at the points immediately surrounding the coordinate center of the black hole (due to the spatial slice excising the physical singularity at this point through [the puncture method](http://gr.physics.ncsu.edu/UMD_June09.pdf)) exhibit numerical errors that drop as $\\left(\\Delta x^i\\right)^4$."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 432x288 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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MWO82m5hYSFLly4lJyfHq+2KVEXAFx7W2nnW2gL31WXA8U7mkfDwyiuvUL9+fVJTUx3LULduXe6//352796tEwFF/ED9jQSSxYsXA3DWWWd5td327dtjrWX+/PlebVekKgK+8CjlSkC7f8Wnjhw5wty5cykqKiI6OtrRLFdffTXGGN577z1Hc4iEIfU34pjc3FwyMjJo2rQpiYmJXm177NixALzzzjtebVekKgKi8DDGLDDGfF/G5dwSj7kbKADKXNjAGDPBGLPKGLNqz549/oouIej999+nsLCQs88+2+/T6JbWvHlzmjZtyuTJk+eBbh4AACAASURBVDXvuogXqL+RYLB06VKstcXn+nnTmWeeiTGGr776yutti1QmyukAANbaoRXdb4y5DBgBnGnL+e/LWjsFmALQq1cv/YcmNfb8888DMGnSJIeTuDRr1ox169bx7bff0q1bN6fjiAQ19TcSDBYtWkRkZCT33nuv19uuW7cuLVq0YNu2bRw8eNDrIyoiFQmIEY+KGGPOAm4HRlprc53OI6EtJyeH1atXk5iYSO/evZ2OA8A111wDwNNPP+1wEpHQpv5GAsXHH39M586dqVevnk/a9xxupVEP8beALzyA54F6wHxjzDpjjOYWFZ+ZMWMGRUVFjBgxwvHDrDzGjRtHREQEH374oQ63EvEt9TfiuEOHDpGWlsbu3bt9to0HH3yQ2NhYTasrflftwsMYU9cYE+mLMGWx1raz1ra01nZzXyb6a9sSfj766COSkpJ44oknnI5SLCEhge7du3Pw4EEtJihhRf2NhKP//ve/APTv399n26hTpw69evXi008/9dk2RMpSaeFhjIkwxowzxnxijMkE0oGdxpgf3Isttfd9TBHf27dvH5988gljx46lRYsWTsf5ndtvv52IiAimTp3qdBQRn1F/IwLvvvsuABdddJFPt7N161Y2bNjAvn37fLodkZKqMuKxCGiLayGlZu69QcnAAFzznD9mjLnEhxlF/OK5557j6NGjDBgwwOko/+PCCy9k9OjRvPvuu+Tl5TkdR8RX1N9I2Pvyyy8BGDq0wnkQau2MM84A0Hoe4ldVKTyGWmsfstamWWuLPDdaa/dZa/9jrb0A0CIDEtSstbz44osA9OzZ0+E0Zbv66qvZv38///73v52OIuIr6m8krO3evZsdO3bQrFkzGjZs6NNt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lyxcuJAzzzyT3NxcunbtytKlS0lKSnI6llTDn//8Z9566y2KioqYPXs255xzjk8WGgo31lpeeOEFRo4cWVx4eFYFDjNh098MGzaMX375hZ9++snpKFKJlStX8tBDDzF+/HimTp1avMaXlG3ixImcdtpp5ObmcsUVV6i4DgKff/45jRo18uksrlUuPKy151lrs621DwD3Aq8Bo3wVLBB49jTm5ubqECAvKSwsZPz48Rw9epTTTjuNJUuW0LhxY6djSQ1ccsklzJo1i6ioKBYsWMDgwYPJzs52OlZQi4iIYOLEifTt27d4soVwXDQznPobz55FHW4V2PLy8jj//PMxxnDNNdc4HScoGGN4//33ue+++5g3bx6vvvqq05GkAtZaPv/8c1JTU4mMjPTZdmo0R6y19gtr7YfW2pA+vqJk4fHTTz8VDxtKzRQWFnL//fezc+dOhg0bxsKFC4vPn5HgdO655zJv3jxiY2NZvnw599xzj9ORglpeXh5r1qzhwIEDxYVHuAvV/qaoqIj09HTatm1L27ZtVXgEuJtuuolt27bRokULunXr5nScoNGiRQvuv/9+hgwZwg033MDmzZudjiTlSEtLY9euXQwbNoxdu3axb98+n2ynKgsIHjLGHHRfDpW4fsgYE9JzAJYsPKTmrLXcddddnHTSSTzyyCNcffXVfPzxx+F6CEnIGTx4MEuXLqVx48ZMmzaNr776SosM1tCPP/5Iz549WbhwYVgWHuHU3zz99NN07NiRzMxM+vfvz7p165yOJOX47LPPeOWVV4iIiGDWrFnF531K1URERNC/f3/y8vI499xzNSFJgPr2228BOO2003j88cdp2bKlT87frMo6HvWstYnuS70S1+tZaxO9niiAlCw8/v73v3P33Xc7nCj4HD16lDFjxvDoo4+yefNm7rvvPl555RWioqKcjiZe1KNHD1atWkWzZs0YOnQobdu21RShNeA5j8xzcnm4FR7h1N8MGjQIgAULFpCYmEhOTo6zgaRMmZmZXHTRRQDce++99OjRw+FEwenGG2+kUaNGfP/99xoZD1CemVvr16/PvHnz6N+/PzExMV7fTnXW8TDGmEuMMfe6r7c0xvTxeqIAUrLw+Oabb5g7d67DiYJLVlYWp59+Oh988AERERG89tprPPjgg2F5zHo4aN26NV9//TVdu3bll19+ITU1lddee83pWEEl3AsPj3Dob3r06EHjxo35/PPPqVu3rgqPAGSt5YorruDw4cP06tWLe++91+lIQatRo0Z88MEHADz++ON8+eWXDieS0jyfQfv372f9+vXOrVxewovAqcA49/XDwAteTxRAShYe9erV06xW1WCtZcCAAaxatYq6deuycOFCrrzySqdjiY81btyYJUuWcPHFF1NUVMSf//xnJk2aRGFhodPRgoLnM8azcnm4Fh6EQX8TGRlJamoq8+bNIz4+nvz8fPLz852OJSU8//zzfPrppzz77LMsXLjQpyfchoPBgwdzyy23AHD++eezf/9+hxNJSZ7TCtq2bcvy5csZN25cJc+omeoUHn2ttdcCRwCstfsB74/BBJC6desCKjxqYsqUKfz0008cf/zxrF27tviwAgl9sbGxTJ8+nYceegiAZ555hptvvtnhVMHBM9Qd7iMehEl/M93X/QAAIABJREFU4zmJ8+BB1+krOp8wcKxdu5abbrqJwYMHc91115GYGFJH+jnm73//OwMHDmT//v1cddVVmmI3gOTk5BAXF0dMTAx9+vThuOOO88l2qlN45BtjIgELYIxJAvxyBqkx5q/GmI3GmB+MMU/4Y5vw+xEPreNRuaKiIh588EFOPfVUJk6cSGpqKmlpabRv397paOJnxhjuueceZs2aRWxsLG+//TZLlixRJ1OJgQMH8uabb5KcnBzuhUdY9DcjRozgo48+olWrVgA63CpAHDx4kLPPPpvCwkJOOukkHR7sRdHR0XzxxRc88cQTzJo1i8mTJzsdSdxycnKIj49n0qRJrF271mfbqU7h8U9gFpBsjHkEWAL83SepSjDGDAbOBbpaazsBT/l6mx4lC4/jjjuOpk2baraecmRnZzNs2DAeeOABli1bxu23387HH39Mw4YNnY4mDho1ahRr1qyhYcOGDBo0iE6dOjFjxgynYwWsdu3acemll1KnTp1wLzzCor9p0qQJI0aMoEGDBoAKj0BgreWSSy5h165dtGvXTtPo+8hNN91Ehw4duOmmm1i1apXTcQTX509UVBTPPPMMGzdu9Nl2qrOA4NvAbcCjwE5glLXWH/9BXAM8Zq096s6R6YdtAr8vPG655RY2b95MRESNlj4JaWlpaXTq1IkFCxYQExPDe++9x2OPPabjYQWAk08+mRUrVnDmmWeyYcMGxowZw/XXX6/j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iIhIIIuIiMj7ZeDll18u8vdPSUnh+eef56effmLTpk3s378ft9udd7xOnTq0adOG/v37c9dddxXrLyQi/q569eo8+uijPProo6xevZqXX36Zb7/9lr179/L222/z9ttvU79+fRITE6latSotWrSgY8eOXHnlldSrV69Iiv/ff/+dRo0aMXXqVC677DKAYp1crsJDRETER+Tv8bDWkpWVRUhIyDm9x9atW4mNjeWXX35h48aNxMXFARASEkJcXBzWWiC7R6NSpUpcfPHFdO/enX79+lGvXr0ivR4RKViLFi2YOnUqAGlpaSxbtoyVK1fy448/snDhQg4ePMjatWvznhMdHU2XLl2oV68ea9asoUWLFrRt25aWLVtSqVKls55rlTtvLCoqSj0eIiIigSwyMpKUlBQAunTpQnp6OrGxscc9x+12s337dn744QdWrFjBxo0bOXr0KLVq1WL79u1s3bo1r7iA7EnrlSpVon379gwePJhq1apx5ZVX0rBhQw2fEvEB4eHhdOrUiU6dOuU9dvToUX755RcWLVrEb7/9RkJCAt999x0ff/wxALNmzcp7blBQEK1bt6ZLly5ER0dz8OBB2rZtS0xMDOXLlz/uXLnzxkqXLp13r1HhISIiEoAiIyPz1v0vVaoU27dv56GHHmLDhg00aNCAbdu2ERsbe9Kkc5fLRVBQEM2aNSMmJoaaNWvSpk0bYmJiqF69ulaeEilhypcvT7du3ejWrdtxj6emprJ582aWLVvGqlWr2Lx5M7t372bnzp2MHTv2uEYHgODgYMqXL0+fPn245ppr2L59O5BdeBw9ehQo3sLDnBigP4iJibErVqxwOgwREZ9hjFlprY1xOg5/U9T5pn///sydO5cGDRqwbt264+ZfRERE0LBhQ8qUKUNUVBTNmzfniiuuICYmhqpVq6q4EAlw6enpeUXJypUr2bhxI7t37yY+Pv6k1bOio6OpWLEiGzduZM2aNVxyySVFFsfp8o16PERERHxEx44dWblyJdWqVeOyyy4jJCSENm3a0LFjR2rWrKniQkROKSwsjObNm9O8efOTjqWmprJ9+3ZiY2NZuXIlWVlZbN68mWbNmlGnTp1ii1E9HiIiAUA9Ht6hfCMicrzT5RttICgiIiIiIl6nwkNERERERLxOhYeIiIiIiHidCg8REREREfE6nyg8jDE3GWM2GGM8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IiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKeU+MhIiIiIiKei4rGw8xqmdknZrbKzL4zs5v8ziQiIsWP6o2IiHcS/A4QogPALc65r80sCVhiZrOdcyv9DiYiIsWK6o2IiEeiYo2Hc26jc+7r4Pl0YBVQw99UIiJS3KjeiIh4Jyoaj+zMrDbQBPgyx/V9zWyxmS3evHmzH9FERKQYUb0REQmvqGo8zKwcMA0Y5JxLy36bc+5551xz51zzypUr+xNQRESKBdUbEZHwi5rGw8wSCRSB15xzb/udR0REiifVGxERb0RF42FmBowHVjnnnvA7j4iIFE+qNyIi3omKxgM4HbgcaG9my4Knc/wOJSIixY7qjYiIR6JiOl3n3GeA+Z1DRESKN9UbERHvRMsaDxERERERiWJqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHNqPERERERExHMJfgcQERERkYIZN24cq1atAiAuLo4BAwbQoEEDn1OJ5E6Nh4iIiEgU2rt3LzfccANlypShdOnSbN26lRIlSjBy5Ei/o4nkKmo2tTKzs8zsezP70cz+43ceEREpnlRvJFr89ttvQGCtx5YtWzj++ONZv369z6lEDi8qGg8ziwfGAWcDJwI9zOxEf1OJiEhxo3oj0SSryahZs+ahf9V4SCSLisYDOBX40Tn3s3NuP/A6cJ7PmUREpPhRvZGokbXGI3vjkXWdSCSKlsajBvBrtsvrg9eJiIiEk+qNRI2stRs1agQ+olmNx8GDB/2MJXJY0dJ4WC7Xub/cwayvmS02s8WbN28uolgiIlLMqN5I1Fi/fj3JyckkJSUBgQYkIyMDfS4lUkVL47EeqJXtck1gQ/Y7OOeed841d841r1y5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XXYZ+/bto1atWsVmv47cxMfH8+abbxIXF8dLL73EZ5995nckiRKhrPGYDHQBZgT/zTo1c85d5mE2kYize/dulixZQmJiIkcddRQnnXRSocZr164dW7ZsIS4ujvnz54cppUjUUr2RqDV58mTmzZtH2bJleemll/yO47kTTzyRu+66C4AePXqwd+9enxNJNAhl5/JU59wvzrkeQBpQlcAc6yeZ2RleBxSJJO+//z579uxhy5YttGvXrtBHoc1aY1KvXj2mTZuGcy4cMUWikuqNRKvff/+dfv36AfDMM89QpUoVnxMVjXvuuYcbb7yR9evXc//99/sdR6JAQqh3NLNrgJuAmsAyoAXwBVDwbU1EoszUqVOpVKkSmzdvLtT+HVnq1atH9erVKVeuHEuWLGHFihU0bqwDNEtsU72RaOKco1evXuzatYs2bdrQq1cvvyMVmcTERMaMGcOePXt47LHH6NKlC6effrrfsSSC5efn2puAfwJrnXPtgCbAZk9SiUSoO++8k0suuQQo3P4dWcyM9u3bs3btWmrXrs1vv/1W6DFFigHVG4kar7zyCnPmzKFnz55Mnjy5WO/bcTjXXXcdcXFxXHDBBezcudPvOBLB8tN47HXO7QUws5LOudVAA29iiUSmxo0bs2PHDo455hgaNAjPxz9rP4+ZM2dy9tmauEcE1RuJEr/88gsDBw6kTZs2TJw4kerVq/sdyReNGzfmuOOO4/fff9dxqeSI8tN4rDezCsB/gdlm9n/ABm9iiUSeZ599lo8++oiPP/6Y9u3bh+1Xraw1Jx999BGZmZn6tUhE9UaiQGZm5qFf+Pv06VPoff6iWWJiItOnTyc+Pp4JEybw/vvv+x1JIlTIfyXOuW7OuR3OufuBe4DxwPleBROJJLt37+aWW25hzJgx/P777/z73/8O29i1a9fmpJNO4vXXX+fYY49l+PDhYRtbJBrFQr159dVXGTJkiN8xpBCGDRvG119/TYUKFTj33HP9juO7E088kUceeQSASy65hD/++MPnRFJQy5Yt49prr+XXX38N+9gFas+dc58652Y45/aHO5BIJHrvvffYvXs3mZmZlC1blq5du4Z1/J49e7Jw4UJq167N1KlTNbuVSFBxrTfffPMNo0ePZteuXX5HkQJYtGjRoVmc3njjDSpVquRvoAhxyy230KJFC3bt2sVVV12lWhal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FBP79u0z/83OmTPnvuOHDh3SderU0Uop7e3trRcsWJCrv2OTyaQvXbqkly9frkePHq1r1aqlXVxcMn1GHB0dde/evfWsWbP0uXPnCqB0D++DDz7QgE5NTdX9+/fX1apVy9fz55RvLH7TLojHoyaCixcvmv9gzp8//0jnEg924MAB/dJLL+mUlBQdGRmpv/zyS/3ll1/qEydOaK21XrNmjXZwcNBNmjTRt27dsnC0tm3JkiXaYDDoJ598UickJOi0tDS9aNEi87/J9evXtdZar1y5Uqemplo4Wts3bNgwrZTKl2VVpeJhnfnmbitWrNCA3r17tz579qyeP3++jomJybfzP4wrV67osWPH6u7du2t3d3dzTrS3t9f169fXo0eP1idPnsyXa02ePNl8/hdeeCHTMZPJpA8cOKABPWvWrHy5nih+TCaTrlKlilZK6VGjRmU6dubMGe3s7Ky9vb31hg0bdFpa2iNfLzExUf/666+6V69eum3btrpChQrmv/ESJUrobt26WTSPLliwQD/77LNaa6179Oih69evn6/nl4pHHmS0wHTr1k0HBwc/8vlE1rZv365LliypK1asmGUlb+3atdrJyUn7+/vryMhIC0RY/MyfP18rpXTHjh11fHz8fcd37NihAd2vXz+dlJRkgQiLh7Vr15qT1U8//fTI55OKh/Xmmwx//vmnBnRISIi59+Po0aP5dv4HOXz4sH7hhRe0l5eX+UtSyZIl9ZAhQ/Ts2bP1li1bCmyfn88++0wD2mg0ZrrXHz58WLu6umpAX7lypUCuLYqHN954QyuldLdu3TI9P3LkSA3oTz75pMCubTKZ9Lp163STJk20g4OD+fNlZ2enu3fvrpctW6bj4uIK7Po5ad++vW7VqlW+njOnfGNdg86sSO/evYH0VWUuXbpk4Whs0/Lly3nyyScpU6YMW7dupVKlSpmOr1mzhu7du1OrVi1CQkIeOClM5I8BAwbw3XffsW7dOnr06HHf8oPNmjXjs88+Y/HixXTr1s28IobIX1euXCEyMhJXV1cZ115M3D3HI+N+V5ATzLXW7N27lw8++IDatWtTr1495syZw/Xr1/Hz8+Ojjz7i1KlT/O9//+OFF16gVatWBTYO/J133qFly5akpaWxZMkS8/NHjx4lLi4Of39/vLy8CuTaonjo1auX+W8+g8lkYsGCBQC0bt26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VF+l1BGllEkpFXjPsfeUUqeUUseVUoW+hendFQ8vL68i1eNx4cIFZs6cSffu3Tl8+DAAVapUoW/fvqxbt46LFy/yxRdfZLlBk9aa1atX06FDB5o1a8bu3buZNGkSp0+f5tVXX8XBwaGwiyNsWJ06dfjtt9/YsWMHTZo04f3336dq1aqMGTMmy8q+0Whk4MCBrF+/3vx37OrqSlJSEgBnzpxh5MiRhISEkJycXNjFyRNPT0+6detG+fLlzfccmeOR/6w53wghhM3TWlv8AfgCtYFNQOBdz/sBBwBHoBpwGjA+6HwBAQE6v/Tr10/XqlVLa631uXPn9JEjR/Lt3AXh2rVreuTIkbpevXoa0ICuWrWqDg4OztX7Y2Ji9KxZs7Sfn58GtI+Pj/700091TExMAUcuxB27d+/WPXr00Eop7eDgoF944QUdFhaW6/f/+uuv2tHRUQPazc1N/+Mf/9BffvmlvnnzZgFGnX9+/vlnDegDBw7k2zmBMG0F93tLP6w53wghhC3IKd9YRY+H1jpca308i0M9gEVa6ySt9VngFNCkMGO7u8ejSpUqWc59sJS4uDjWr1/PBx98wJw5c4D0lYO+//57vL29mTJlCkePHuXMmTN07tw5x3MdOHCAV155hfLlyzNs2DDs7OyYN28e586d49///jdubm6FUSQhAGjcuDG///47x48fZ8iQISxevJjAwEAaN27Md999R0xMTI7v79WrFzdu3GD58uX079+f8PBwXn/9ddLS0gD4448/+O677zh58qRVTlCXoVYFx5rzjRBC2DqrqHjkoAJw4a6f/7793H2UUsOUUmFKqbB7V8d5FBmTy7XWXL58mS+//JLz58/n2/nzYvz48TRr1oySJUsSFBTEpEmTzCtTubm5ERERwbp163j77bfx9fXNdh7GlStXmDZtGv7+/jRo0IDvv/+eHj16sHXrVvbv38/AgQNlSJWwqJo1a/L1119z8eJFvvrqKxISEhg6dCje3t4MGDCAdevWkZqamuV7XV1d6d69O99++y2nTp3iwoUL5i/yCxYsYOjQodSqVQsfHx/69u3LzJkzC7No9+nbty+dOqWP7pGKh0VYPN8IIYStsyusCymlQgDvLA6N1lovy+5tWTyXZfOk1noWMAsgMDAw35owS5UqRVpaGjExMVy6dInXX3+dKlWqULly5fy6RJYSExM5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\n",
"text/plain": [
"<Figure size 864x864 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"x48,y48,valuesCF48,valuesHam48,valuesmomr48,valuesmomtheta48,valuesmomphi48 = np.loadtxt('out48.txt').T #Transposed for easier unpacking\n",
"points48 = np.zeros((len(x48), 2))\n",
"for i in range(len(x48)):\n",
" points48[i][0] = x48[i]\n",
" points48[i][1] = y48[i]\n",
"\n",
"RefData=[valuesHam48,valuesmomr48,valuesmomtheta48,valuesmomphi48]\n",
"SubTitles=[\"\\mathcal{H}\",'\\mathcal{M}^r',r\"\\mathcal{M}^{\\theta}\",\"\\mathcal{M}^{\\phi}\"]\n",
"axN = []\n",
"plt.clf()\n",
"\n",
"# We want to create four plots. One for the Hamiltonian, and three for the momentum\n",
"# constrains (r,th,ph)\n",
"# Define the size of the overall figure\n",
"fig = plt.figure(figsize=(12,12)) # 8 in x 8 in\n",
"\n",
"for p in range(num_plots): #loop to cycle through our constraints and plot the data\n",
" grid48 = griddata(points48, RefData[p], (grid_x, grid_y), method='nearest')\n",
" griddiff_48_minus_96 = np.zeros((100,100))\n",
" griddiff_48_minus_96_1darray = np.zeros(100*100)\n",
" gridx_1darray_yeq0 = np.zeros(100)\n",
" grid48_1darray_yeq0 = np.zeros(100)\n",
" grid96_1darray_yeq0 = np.zeros(100)\n",
" count = 0\n",
" for i in range(100):\n",
" for j in range(100):\n",
" griddiff_48_minus_96[i][j] = grid48[i][j] - grid96N[p][i][j]\n",
" griddiff_48_minus_96_1darray[count] = griddiff_48_minus_96[i][j]\n",
" if j==49:\n",
" gridx_1darray_yeq0[i] = grid_x[i][j]\n",
" grid48_1darray_yeq0[i] = grid48[i][j] + np.log10((48./96.)**4)\n",
" grid96_1darray_yeq0[i] = grid96N[p][i][j]\n",
" count = count + 1\n",
"\n",
" #Generate the subplot for the each constraint\n",
" ax = fig.add_subplot(221+p)\n",
" axN.append(ax) # Grid of 2x2\n",
" axN[p].set_title('Plot Demonstrating $4^{th}$-Order Convergence of $'+SubTitles[p]+'$')\n",
" axN[p].set_xlabel(\"x/M\")\n",
" axN[p].set_ylabel(\"$log_{10}$(Relative Error)\")\n",
"\n",
" ax.plot(gridx_1darray_yeq0, grid96_1darray_yeq0, 'k-', label='Nr=96')\n",
" ax.plot(gridx_1darray_yeq0, grid48_1darray_yeq0, 'k--', label='Nr=48, mult by (48/96)^4')\n",
" ax.set_ylim([-14,4.])\n",
"\n",
" legend = ax.legend(loc='lower right', shadow=True, fontsize='x-large')\n",
" legend.get_frame().set_facecolor('C1')\n",
"\n",
"# Adjust the spacing between plots\n",
"plt.tight_layout(pad=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='latex_pdf_output'></a>\n",
"\n",
"# Step 7: 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-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.pdf](Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.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": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\r\n",
" restricted \\write18 enabled.\r\n",
"entering extended mode\r\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\r\n",
" restricted \\write18 enabled.\r\n",
"entering extended mode\r\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\r\n",
" restricted \\write18 enabled.\r\n",
"entering extended mode\r\n"
]
}
],
"source": [
"!jupyter nbconvert --to latex --template latex_nrpy_style.tplx --log-level='WARN' Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.ipynb\n",
"!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Start_to_Finish-BSSNCurvilinear-Setting_up_Exact_Initial_Data.tex\n",
"!rm -f Tut*.out Tut*.aux Tut*.log"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
# As documented in the NRPy+ tutorial module
# Tutorial-ADMBSSN_tofrom_4metric.ipynb,
# this module will construct expressions for
# ADM or BSSN quantities in terms of the
# 4-metric g4DD, and g4DD/g4UU in terms of
# ADM/BSSN quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import sys # Standard Python modules for multiplatform OS-level functions
def setup_ADM_quantities(inputvars):
if inputvars == "ADM":
gammaDD = ixp.declarerank2("gammaDD", "sym01")
betaU = ixp.declarerank1("betaU")
alpha = sp.symbols("alpha", real=True)
elif inputvars == "BSSN":
import BSSN.ADM_in_terms_of_BSSN as AitoB
# Construct gamma_{ij} in terms of cf & gammabar_{ij}
AitoB.ADM_in_terms_of_BSSN()
gammaDD = AitoB.gammaDD
# Next construct beta^i in terms of vet^i and reference metric quantities
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
betaU = Bq.betaU
alpha = sp.symbols("alpha", real=True)
else:
print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.")
sys.exit(1)
return gammaDD,betaU,alpha
# g_{mu nu} in terms of BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") variables.
def g4DD_ito_BSSN_or_ADM(inputvars,gammaDD=None,betaU=None,alpha=None):
# Step 0: Declare g4DD as globals, to make interfacing with other modules/functions easier
global g4DD
# Step 1: Set gammaDD, betaU, and alpha if not already input.
if gammaDD==None and betaU==None and alpha==None:
gammaDD,betaU,alpha = setup_ADM_quantities(inputvars)
# Step 2: Compute g4DD = g_{mu nu}:
# To get \gamma_{\mu \nu} = gamma4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S:
g4DD = ixp.zerorank2(DIM=4)
# Step 2.a: Compute beta_i via Eq. 2.121 in B&S
betaD = ixp.zerorank1()
for i in range(3):
for j in range(3):
betaD[i] += gammaDD[i][j] * betaU[j]
# Step 2.b: Compute beta_i beta^i, the beta contraction.
beta2 = sp.sympify(0)
for i in range(3):
beta2 += betaU[i] * betaD[i]
# Step 2.c: Construct g4DD via Eq. 2.122 in B&S
g4DD[0][0] = -alpha ** 2 + beta2
for mu in range(1, 4):
g4DD[mu][0] = g4DD[0][mu] = betaD[mu - 1]
for mu in range(1, 4):
for nu in range(1, 4):
g4DD[mu][nu] = gammaDD[mu - 1][nu - 1]
# g^{mu nu} in terms of BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") variables.
def g4UU_ito_BSSN_or_ADM(inputvars,gammaDD=None,betaU=None,alpha=None, gammaUU=None):
# Step 0: Declare g4UU as globals, to make interfacing with other modules/functions easier
global g4UU
# Step 1: Set gammaDD, betaU, and alpha if not already input.
if gammaDD==None and betaU==None and alpha==None:
gammaDD,betaU,alpha = setup_ADM_quantities(inputvars)
# Step 2: Compute g4UU = g_{mu nu}:
# To get \gamma^{\mu \nu} = gamma4UU[mu][nu], we'll need to use Eq. 2.119 in B&S.
g4UU = ixp.zerorank2(DIM=4)
# Step 3: Construct g4UU = g^{mu nu}
# Step 3.a: Compute gammaUU based on provided gammaDD:
if gammaUU==None:
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
# Then evaluate g4UU:
g4UU = ixp.zerorank2(DIM=4)
g4UU[0][0] = -1 / alpha ** 2
for mu in range(1, 4):
g4UU[0][mu] = g4UU[mu][0] = betaU[mu - 1] / alpha ** 2
for mu in range(1, 4):
for nu in range(1, 4):
g4UU[mu][nu] = gammaUU[mu - 1][nu - 1] - betaU[mu - 1] * betaU[nu - 1] / alpha ** 2
# BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") metric variables in terms of g_{mu nu}
def BSSN_or_ADM_ito_g4DD(inputvars,g4DD=None):
# Step 0: Declare output variables as globals, to make interfacing with other modules/functions easier
if inputvars == "ADM":
global gammaDD, betaU, alpha
elif inputvars == "BSSN":
global hDD, cf, vetU, alpha
else:
print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.")
sys.exit(1)
# Step 1: declare g4DD as symmetric rank-4 tensor:
g4DD_is_input_into_this_function = True
if g4DD == None:
g4DD = ixp.declarerank2("g4DD", "sym01", DIM=4)
g4DD_is_input_into_this_function = False
# Step 2: Compute gammaDD & betaD
betaD = ixp.zerorank1()
gammaDD = ixp.zerorank2()
for i in range(3):
betaD[i] = g4DD[0][i]
for j in range(3):
gammaDD[i][j] = g4DD[i + 1][j + 1]
# Step 3: Compute betaU
# Step 3.a: Compute gammaUU based on provided gammaDD
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
# Step 3.b: Use gammaUU to raise betaU
betaU = ixp.zerorank1()
for i in range(3):
for j in range(3):
betaU[i] += gammaUU[i][j] * betaD[j]
# Step 4: Compute alpha = sqrt(beta^2 - g_{00}):
# Step 4.a: Compute beta^2 = beta^k beta_k:
beta_squared = sp.sympify(0)
for k in range(3):
beta_squared += betaU[k] * betaD[k]
# Step 4.b: alpha = sqrt(beta^2 - g_{00}):
if g4DD_is_input_into_this_function == False:
alpha = sp.sqrt(sp.simplify(beta_squared) - g4DD[0][0])
else:
alpha = sp.sqrt(beta_squared - g4DD[0][0])
# Step 5: If inputvars == "ADM", we are finished. Return.
if inputvars == "ADM":
return
# Step 6: If inputvars == "BSSN", convert ADM to BSSN
import BSSN.BSSN_in_terms_of_ADM as BitoA
dummyBU = ixp.zerorank1()
BitoA.gammabarDD_hDD(gammaDD)
BitoA.cf_from_gammaDD(gammaDD)
BitoA.betU_vetU(betaU, dummyBU)
hDD = BitoA.hDD
cf = BitoA.cf
vetU = BitoA.vetU
# This module performs the conversion between ADM
# spacetime variables in Spherical or Cartesian coordinates
# given as *exact* SymPy expressions (i.e., direct functions
# of r,th,ph or x,y,z), to rescaled BSSN-in-curvilinear
# coordinate quantities, as defined in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P0: Import needed Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python module for multiplatform OS-level functions
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, Sph_r_th_ph_or_Cart_xyz,
gammaDD_inSphorCart, KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart):
# This routine converts the ADM variables
# $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$
# in Spherical or Cartesian basis+coordinates, first to the BSSN variables
# in the chosen reference_metric::CoordSystem coordinate system+basis:
# $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$
# and then to the rescaled variables:
# $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step P1: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step P2: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc.
# This ensures that the input arrays are not modified below;
# modifying them would result in unexpected effects outside
# this function.
alphaSphorCart = alpha_inSphorCart
betaSphorCartU = ixp.zerorank1()
BSphorCartU = ixp.zerorank1()
gammaSphorCartDD = ixp.zerorank2()
KSphorCartDD = ixp.zerorank2()
for i in range(DIM):
betaSphorCartU[i] = betaU_inSphorCart[i]
BSphorCartU[i] = BU_inSphorCart[i]
for j in range(DIM):
gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j]
KSphorCartDD[i][j] = KDD_inSphorCart[i][j]
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
# Step 1: All input quantitiefs are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
# Note that substitution only works when the variable is not an integer. Hence the
# if isinstance(...,...) stuff:
def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz, rthph_or_xyz_of_xx):
if isinstance(obj, int):
return sp.sympify(obj)
else:
return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\
subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\
subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2])
r_th_ph_or_Cart_xyz_of_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_of_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz(
alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
for i in range(DIM):
betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
for j in range(DIM):
gammaSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
# Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_of_xx[i],rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i]*Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i]*Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
# basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
# we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
# Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear
# counterparts:
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.gammabarDD_hDD(gammaDD)
BitoA.trK_AbarDD_aDD(gammaDD, KDD)
BitoA.LambdabarU_lambdaU__exact_gammaDD(gammaDD)
BitoA.cf_from_gammaDD(gammaDD)
BitoA.betU_vetU(betaU, BU)
# Step 4: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2
# basis, and as functions of the consistent xx0,xx1,xx2 coordinates.
return BitoA.cf, BitoA.hDD, BitoA.lambdaU, BitoA.aDD, BitoA.trK, alpha, BitoA.vetU, BitoA.betU
# This module performs the conversion between ADM
# spacetime variables in Spherical or Cartesian coordinates
# given as *numerical* expressions (i.e., ADM quantities
# are only given to floating-point precision; e.g., in the
# case of an initial data solver), to rescaled BSSN-in-curvilinear
# coordinate quantities, as defined in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: Initialize core Python/NRPy+ modules
from outputC import * # NRPy+: Core C code output module
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities; e.g., gammabarUU & GammabarUDD needed below
import os, sys # Standard Python modules for multiplatform OS-level functions
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, ADM_input_function_name,
Ccodesdir = "BSSN", pointer_to_ID_inputs=False,loopopts=",oldloops"):
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step 1: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
# Define the input variables:
gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01")
alphaSphorCart = sp.symbols("alphaSphorCart")
betaSphorCartU = ixp.declarerank1("betaSphorCartU")
BSphorCartU = ixp.declarerank1("BSphorCartU")
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
r_th_ph_or_Cart_xyz_oID_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
# Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
# basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
# we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
# Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear
# counterparts, for all BSSN quantities *except* lambda^i:
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.gammabarDD_hDD(gammaDD)
BitoA.trK_AbarDD_aDD(gammaDD, KDD)
BitoA.cf_from_gammaDD(gammaDD)
BitoA.betU_vetU(betaU, BU)
hDD = BitoA.hDD
trK = BitoA.trK
aDD = BitoA.aDD
cf = BitoA.cf
vetU = BitoA.vetU
betU = BitoA.betU
# Step 4: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
# [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
# from finite-difference derivatives of rescaled metric
# quantities $h_{ij}$:
# \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).
# The reference_metric.py module provides us with analytic expressions for
# $\hat{\Gamma}^i_{jk}$, so here we need only compute
# finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
# the values for $h_{ij}$ provided in the initial data. Once
# $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
# procedure:
# \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},
# and then output the result to a C file using the NRPy+
# finite-difference C output routine.
# We will need all BSSN gridfunctions to be defined, as well as
# expressions for gammabarDD_dD in terms of exact derivatives of
# the rescaling matrix and finite-difference derivatives of
# hDD's. This functionality is provided by BSSN.BSSN_unrescaled_and_barred_vars,
# which we call here to overwrite above definitions of gammabarDD,gammabarUU, etc.
Bq.gammabar__inverse_and_derivs() # Provides gammabarUU and GammabarUDD
gammabarUU = Bq.gammabarUU
GammabarUDD = Bq.GammabarUDD
# Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD
# (from the reference metric):
LambdabarU = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k])
# Finally apply rescaling:
# lambda^i = Lambdabar^i/\text{ReU[i]}
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
if ADM_input_function_name == "DoNotOutputADMInputFunction":
return hDD,aDD,trK,vetU,betU,alpha,cf,lambdaU
# Step 5.A: Output files containing finite-differenced lambdas.
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])]
desc = "Output lambdaU[i] for BSSN, built using finite-difference derivatives."
name = "ID_BSSN_lambdas"
params = "const paramstruct *restrict params,REAL *restrict xx[3],REAL *restrict in_gfs"
preloop = ""
opts = ""
idx4replace = "IDX4S"
if "oldloops" in loopopts:
params = "const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs"
opts = "DisableCparameters"
idx4replace = "IDX4"
preloop = """
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
"""
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
preloop=preloop,
body=fin.FD_outputC("returnstring", lambdaU_expressions, outCparams).replace("IDX4",idx4replace),
loopopts="InteriorPoints,Read_xxs"+loopopts, opts=opts)
# Step 5: Output all ADM-to-BSSN expressions to a C function. This function
# must first call the ID_ADM_SphorCart() defined above. Using these
# Spherical or Cartesian data, it sets up all quantities needed for
# BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
# computed from numerical data using finite-difference derivatives.
ID_inputs_param = "ID_inputs other_inputs,"
if pointer_to_ID_inputs == True:
ID_inputs_param = "ID_inputs *other_inputs,"
desc = "Write BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"
name = "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs"
opts = ""
params = "const paramstruct *restrict params, "
if "oldloops" in loopopts:
opts = "DisableCparameters"
params = ""
params += "const REAL xx0xx1xx2[3]," + ID_inputs_param + """
REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
REAL *trK,
REAL *vetU0,REAL *vetU1,REAL *vetU2,
REAL *betU0,REAL *betU1,REAL *betU2,
REAL *alpha, REAL *cf"""
outCparams = "preindent=1,outCverbose=False,includebraces=False"
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
body="""
REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
const REAL xx0 = xx0xx1xx2[0];
const REAL xx1 = xx0xx1xx2[1];
const REAL xx2 = xx0xx1xx2[2];
REAL xyz_or_rthph[3];\n""" +
outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
"returnstring",
outCparams + ",CSE_enable=False") + " " + ADM_input_function_name + """(xyz_or_rthph, other_inputs,
&gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
&gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
&KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
&KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
&alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
&BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
// Next compute all rescaled BSSN curvilinear quantities:\n""" +
outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2],
trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2],
alpha, cf],
["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22",
"*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22",
"*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2",
"*alpha", "*cf"], "returnstring", params=outCparams),
opts = opts)
# Step 5.a: Output the driver function for the above
# function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
# Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
desc = """Driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(),
which writes BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"""
name = "ID_BSSN__ALL_BUT_LAMBDAs"
params = "const paramstruct *restrict params,REAL *restrict xx[3]," + ID_inputs_param + "REAL *in_gfs"
opts = ""
funccallparams = "params, "
idx3replace = "IDX3S"
idx4ptreplace = "IDX4ptS"
if "oldloops" in loopopts:
params = "const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," + ID_inputs_param + "REAL *in_gfs"
opts = "DisableCparameters"
funccallparams = ""
idx3replace = "IDX3"
idx4ptreplace = "IDX4pt"
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
body="""
const int idx = IDX3(i0,i1,i2);
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(""".replace("IDX3",idx3replace)+funccallparams+"""xx0xx1xx2,other_inputs,
&in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
&in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
&in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
&in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
&in_gfs[IDX4pt(TRKGF,idx)],
&in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
&in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
&in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
""".replace("IDX4pt",idx4ptreplace),
loopopts="AllPoints,Read_xxs"+loopopts, opts=opts)
# As documented in the NRPy+ tutorial module
# Tutorial-ADM_in_terms_of_BSSN.ipynb,
# this module will construct expressions for ADM
# quantities in terms of BSSN quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
from outputC import * # NRPy+: Core C code output module
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python module for multiplatform OS-level functions
def ADM_in_terms_of_BSSN():
global gammaDD, gammaDDdD, gammaDDdDD, gammaUU, detgamma, GammaUDD, KDD, KDDdD
# Step 1.c: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.e: Import all basic (unrescaled) BSSN scalars & tensors
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
cf = Bq.cf
AbarDD = Bq.AbarDD
trK = Bq.trK
Bq.gammabar__inverse_and_derivs()
gammabarDD_dD = Bq.gammabarDD_dD
gammabarDD_dDD = Bq.gammabarDD_dDD
Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
AbarDD_dD = Bq.AbarDD_dD
# Step 2: The ADM three-metric gammaDD and its
# derivatives in terms of BSSN quantities.
gammaDD = ixp.zerorank2()
exp4phi = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
exp4phi = sp.exp(4 * cf)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
exp4phi = (1 / cf)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
exp4phi = (1 / cf ** 2)
else:
print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
sys.exit(1)
for i in range(DIM):
for j in range(DIM):
gammaDD[i][j] = exp4phi * gammabarDD[i][j]
# Step 2.a: Derivatives of $e^{4\phi}$
phidD = ixp.zerorank1()
phidDD = ixp.zerorank2()
cf_dD = ixp.declarerank1("cf_dD")
cf_dDD = ixp.declarerank2("cf_dDD","sym01")
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
for i in range(DIM):
phidD[i] = cf_dD[i]
for j in range(DIM):
phidDD[i][j] = cf_dDD[i][j]
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
for i in range(DIM):
phidD[i] = -sp.Rational(1,4)*exp4phi*cf_dD[i]
for j in range(DIM):
phidDD[i][j] = sp.Rational(1,4)*( exp4phi**2*cf_dD[i]*cf_dD[j] - exp4phi*cf_dDD[i][j] )
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
exp2phi = (1 / cf)
for i in range(DIM):
phidD[i] = -sp.Rational(1,2)*exp2phi*cf_dD[i]
for j in range(DIM):
phidDD[i][j] = sp.Rational(1,2)*( exp4phi*cf_dD[i]*cf_dD[j] - exp2phi*cf_dDD[i][j] )
else:
print("Error EvolvedConformalFactor_cf type = \""+par.parval_from_str("EvolvedConformalFactor_cf")+"\" unknown.")
sys.exit(1)
exp4phidD = ixp.zerorank1()
exp4phidDD = ixp.zerorank2()
for i in range(DIM):
exp4phidD[i] = 4*exp4phi*phidD[i]
for j in range(DIM):
exp4phidDD[i][j] = 16*exp4phi*phidD[i]*phidD[j] + 4*exp4phi*phidDD[i][j]
# Step 2.b: Derivatives of gammaDD, the ADM three-metric
gammaDDdD = ixp.zerorank3()
gammaDDdDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammaDDdD[i][j][k] = exp4phidD[k] * gammabarDD[i][j] + exp4phi * gammabarDD_dD[i][j][k]
for l in range(DIM):
gammaDDdDD[i][j][k][l] = exp4phidDD[k][l] * gammabarDD[i][j] + \
exp4phidD[k] * gammabarDD_dD[i][j][l] + \
exp4phidD[l] * gammabarDD_dD[i][j][k] + \
exp4phi * gammabarDD_dDD[i][j][k][l]
# Step 2.c: 3-Christoffel symbols associated with ADM 3-metric gammaDD
# Step 2.c.i: First compute the inverse 3-metric gammaUU:
gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
GammaUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammaUDD[i][j][k] += sp.Rational(1,2)*gammaUU[i][l]* \
(gammaDDdD[l][j][k] + gammaDDdD[l][k][j] - gammaDDdD[j][k][l])
# Step 3: Define ADM extrinsic curvature KDD and
# its first spatial derivatives KDDdD
# in terms of BSSN quantities
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
KDD[i][j] = exp4phi * AbarDD[i][j] + sp.Rational(1, 3) * gammaDD[i][j] * trK
KDDdD = ixp.zerorank3()
trK_dD = ixp.declarerank1("trK_dD")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
KDDdD[i][j][k] = exp4phidD[k] * AbarDD[i][j] + exp4phi * AbarDD_dD[i][j][k] + \
sp.Rational(1, 3) * (gammaDDdD[i][j][k] * trK + gammaDD[i][j] * trK_dD[k])
# This module sets up a standard initial data function used for
# setting up SENR initial data at all gridpoints.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
from outputC import *
def BSSN_ID_function_string(cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU):
rhss = [trK,alpha,cf]
lhss = ["in_gfs[IDX4S(TRKGF,i0,i1,i2)]","in_gfs[IDX4S(ALPHAGF,i0,i1,i2)]","in_gfs[IDX4S(CFGF,i0,i1,i2)]"]
for i in range(3):
rhss.append(lambdaU[i])
lhss.append("in_gfs[IDX4S(LAMBDAU"+str(i)+"GF,i0,i1,i2)]")
rhss.append(vetU[i])
lhss.append("in_gfs[IDX4S(VETU"+str(i)+"GF,i0,i1,i2)]")
rhss.append(betU[i])
lhss.append("in_gfs[IDX4S(BETU"+str(i)+"GF,i0,i1,i2)]")
for j in range(i,3):
rhss.append(hDD[i][j])
lhss.append("in_gfs[IDX4S(HDD" + str(i) + str(j) + "GF,i0,i1,i2)]")
rhss.append(aDD[i][j])
lhss.append("in_gfs[IDX4S(ADD" + str(i) + str(j) + "GF,i0,i1,i2)]")
# Sort the lhss list alphabetically, and rhss to match:
lhss,rhss = [list(x) for x in zip(*sorted(zip(lhss, rhss), key=lambda pair: pair[0]))]
body = outputC(rhss, lhss, filename="returnstring",
params="preindent=1,CSE_enable=True,outCverbose=False", # outCverbose=False to prevent
# enormous output files.
prestring="", poststring="")
desc = "Set up the initial data at all points on the numerical grid."
add_to_Cfunction_dict(
desc =desc,
name ="initial_data",
params ="const paramstruct *restrict params,REAL *restrict xx[3], REAL *restrict in_gfs",
body =body,
loopopts="AllPoints,Read_xxs")
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_RHSs.ipynb,
# this module will construct the right-hand sides (RHSs)
# expressions of the BSSN time evolution equations.
#
# Time-evolution equations for the BSSN gauge conditions are
# specified in the BSSN_gauge_RHSs module and documented in
# the Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.ipynb
# NRPy+ tutorial module.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
import BSSN.BSSN_quantities as Bq # NRPy+: This module depends on the parameter EvolvedConformalFactor_cf,
# which is defined in BSSN.BSSN_quantities
have_already_called_BSSN_RHSs_function = False
# Step 1.b: Set the coordinate system for the numerical grid:
# DO NOT SET IN STANDALONE PYTHON MODULE
# par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
def BSSN_RHSs():
# Step 1.c: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
global have_already_called_BSSN_RHSs_function # setting to global enables other modules to see updated value.
have_already_called_BSSN_RHSs_function = True
# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.e: Import all basic (unrescaled) BSSN scalars & tensors
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
AbarDD = Bq.AbarDD
LambdabarU = Bq.LambdabarU
trK = Bq.trK
alpha = Bq.alpha
betaU = Bq.betaU
# Step 1.f: Import all neeeded rescaled BSSN tensors:
aDD = Bq.aDD
cf = Bq.cf
lambdaU = Bq.lambdaU
# Step 2.a.i: Import derivative expressions for betaU defined in the BSSN.BSSN_quantities module:
Bq.betaU_derivs()
betaU_dD = Bq.betaU_dD
betaU_dDD = Bq.betaU_dDD
# Step 2.a.ii: Import derivative expression for gammabarDD
Bq.gammabar__inverse_and_derivs()
gammabarDD_dupD = Bq.gammabarDD_dupD
# Step 2.a.iii: First term of \partial_t \bar{\gamma}_{i j} right-hand side:
# \beta^k \bar{\gamma}_{ij,k} + \beta^k_{,i} \bar{\gamma}_{kj} + \beta^k_{,j} \bar{\gamma}_{ik}
gammabar_rhsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammabar_rhsDD[i][j] += betaU[k] * gammabarDD_dupD[i][j][k] + betaU_dD[k][i] * gammabarDD[k][j] \
+ betaU_dD[k][j] * gammabarDD[i][k]
# Step 2.b.i: First import \bar{A}_{ij} = AbarDD[i][j], and its contraction trAbar = \bar{A}^k_k
# from BSSN.BSSN_quantities
Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
trAbar = Bq.trAbar
# Step 2.b.ii: Import detgammabar quantities from BSSN.BSSN_quantities:
Bq.detgammabar_and_derivs()
detgammabar = Bq.detgammabar
detgammabar_dD = Bq.detgammabar_dD
# Step 2.b.ii: Compute the contraction \bar{D}_k \beta^k = \beta^k_{,k} + \frac{\beta^k \bar{\gamma}_{,k}}{2 \bar{\gamma}}
Dbarbetacontraction = sp.sympify(0)
for k in range(DIM):
Dbarbetacontraction += betaU_dD[k][k] + betaU[k] * detgammabar_dD[k] / (2 * detgammabar)
# Step 2.b.iii: Second term of \partial_t \bar{\gamma}_{i j} right-hand side:
# \frac{2}{3} \bar{\gamma}_{i j} \left (\alpha \bar{A}_{k}^{k} - \bar{D}_{k} \beta^{k}\right )
for i in range(DIM):
for j in range(DIM):
gammabar_rhsDD[i][j] += sp.Rational(2, 3) * gammabarDD[i][j] * (alpha * trAbar - Dbarbetacontraction)
# Step 2.c: Third term of \partial_t \bar{\gamma}_{i j} right-hand side:
# -2 \alpha \bar{A}_{ij}
for i in range(DIM):
for j in range(DIM):
gammabar_rhsDD[i][j] += -2 * alpha * AbarDD[i][j]
# Step 3.a: First term of \partial_t \bar{A}_{i j}:
# \beta^k \partial_k \bar{A}_{ij} + \partial_i \beta^k \bar{A}_{kj} + \partial_j \beta^k \bar{A}_{ik}
# First define AbarDD_dupD:
AbarDD_dupD = Bq.AbarDD_dupD # From Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
Abar_rhsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Abar_rhsDD[i][j] += betaU[k] * AbarDD_dupD[i][j][k] + betaU_dD[k][i] * AbarDD[k][j] \
+ betaU_dD[k][j] * AbarDD[i][k]
# Step 3.b: Second term of \partial_t \bar{A}_{i j}:
# - (2/3) \bar{A}_{i j} \bar{D}_{k} \beta^{k} - 2 \alpha \bar{A}_{i k} {\bar{A}^{k}}_{j} + \alpha \bar{A}_{i j} K
gammabarUU = Bq.gammabarUU # From Bq.gammabar__inverse_and_derivs()
AbarUD = Bq.AbarUD # From Bq.AbarUU_AbarUD_trAbar()
for i in range(DIM):
for j in range(DIM):
Abar_rhsDD[i][j] += -sp.Rational(2, 3) * AbarDD[i][j] * Dbarbetacontraction + alpha * AbarDD[i][j] * trK
for k in range(DIM):
Abar_rhsDD[i][j] += -2 * alpha * AbarDD[i][k] * AbarUD[k][j]
# Step 3.c.i: Define partial derivatives of \phi in terms of evolved quantity "cf":
Bq.phi_and_derivs()
phi_dD = Bq.phi_dD
phi_dupD = Bq.phi_dupD
phi_dDD = Bq.phi_dDD
exp_m4phi = Bq.exp_m4phi
phi_dBarD = Bq.phi_dBarD # phi_dBarD = Dbar_i phi = phi_dD (since phi is a scalar)
phi_dBarDD = Bq.phi_dBarDD # phi_dBarDD = Dbar_i Dbar_j phi (covariant derivative)
# Step 3.c.ii: Define RbarDD
Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
RbarDD = Bq.RbarDD
# Step 3.c.iii: Define first and second derivatives of \alpha, as well as
# \bar{D}_i \bar{D}_j \alpha, which is defined just like phi
alpha_dD = ixp.declarerank1("alpha_dD")
alpha_dDD = ixp.declarerank2("alpha_dDD", "sym01")
alpha_dBarD = alpha_dD
alpha_dBarDD = ixp.zerorank2()
GammabarUDD = Bq.GammabarUDD # Defined in Bq.gammabar__inverse_and_derivs()
for i in range(DIM):
for j in range(DIM):
alpha_dBarDD[i][j] = alpha_dDD[i][j]
for k in range(DIM):
alpha_dBarDD[i][j] += - GammabarUDD[k][i][j] * alpha_dD[k]
# Step 3.c.iv: Define the terms in curly braces:
curlybrackettermsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
curlybrackettermsDD[i][j] = -2 * alpha * phi_dBarDD[i][j] + 4 * alpha * phi_dBarD[i] * phi_dBarD[j] \
+ 2 * alpha_dBarD[i] * phi_dBarD[j] \
+ 2 * alpha_dBarD[j] * phi_dBarD[i] \
- alpha_dBarDD[i][j] + alpha * RbarDD[i][j]
# Step 3.c.v: Compute the trace:
curlybracketterms_trace = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
curlybracketterms_trace += gammabarUU[i][j] * curlybrackettermsDD[i][j]
# Step 3.c.vi: Third and final term of Abar_rhsDD[i][j]:
for i in range(DIM):
for j in range(DIM):
Abar_rhsDD[i][j] += exp_m4phi * (curlybrackettermsDD[i][j] -
sp.Rational(1, 3) * gammabarDD[i][j] * curlybracketterms_trace)
# Step 4: Right-hand side of conformal factor variable "cf". Supported
# options include: cf=phi, cf=W=e^(-2*phi) (default), and cf=chi=e^(-4*phi)
# \partial_t phi = \left[\beta^k \partial_k \phi \right] <- TERM 1
# + \frac{1}{6} \left (\bar{D}_{k} \beta^{k} - \alpha K \right ) <- TERM 2
global cf_rhs
cf_rhs = sp.Rational(1, 6) * (Dbarbetacontraction - alpha * trK) # Term 2
for k in range(DIM):
cf_rhs += betaU[k] * phi_dupD[k] # Term 1
# Next multiply to convert phi_rhs to cf_rhs.
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
pass # do nothing; cf_rhs = phi_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
cf_rhs *= -2 * cf # cf_rhs = -2*cf*phi_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi":
cf_rhs *= -4 * cf # cf_rhs = -4*cf*phi_rhs
else:
print("Error: EvolvedConformalFactor_cf == " +
par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") + " unsupported!")
sys.exit(1)
# Step 5: right-hand side of trK (trace of extrinsic curvature):
# \partial_t K = \beta^k \partial_k K <- TERM 1
# + \frac{1}{3} \alpha K^{2} <- TERM 2
# + \alpha \bar{A}_{i j} \bar{A}^{i j} <- TERM 3
# - - e^{-4 \phi} (\bar{D}_{i} \bar{D}^{i} \alpha + 2 \bar{D}^{i} \alpha \bar{D}_{i} \phi ) <- TERM 4
global trK_rhs
# TERM 2:
trK_rhs = sp.Rational(1, 3) * alpha * trK * trK
trK_dupD = ixp.declarerank1("trK_dupD")
for i in range(DIM):
# TERM 1:
trK_rhs += betaU[i] * trK_dupD[i]
for i in range(DIM):
for j in range(DIM):
# TERM 4:
trK_rhs += -exp_m4phi * gammabarUU[i][j] * (alpha_dBarDD[i][j] + 2 * alpha_dBarD[j] * phi_dBarD[i])
AbarUU = Bq.AbarUU # From Bq.AbarUU_AbarUD_trAbar()
for i in range(DIM):
for j in range(DIM):
# TERM 3:
trK_rhs += alpha * AbarDD[i][j] * AbarUU[i][j]
# Step 6: right-hand side of \partial_t \bar{\Lambda}^i:
# \partial_t \bar{\Lambda}^i = \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k <- TERM 1
# + \bar{\gamma}^{j k} \hat{D}_{j} \hat{D}_{k} \beta^{i} <- TERM 2
# + \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j} <- TERM 3
# + \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j} <- TERM 4
# - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \partial_{j} \phi) <- TERM 5
# + 2 \alpha \bar{A}^{j k} \Delta_{j k}^{i} <- TERM 6
# - \frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K <- TERM 7
# Step 6.a: Term 1 of \partial_t \bar{\Lambda}^i: \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k
# First we declare \bar{\Lambda}^i and \bar{\Lambda}^i_{,j} in terms of \lambda^i and \lambda^i_{,j}
global LambdabarU_dupD # Used on the RHS of the Gamma-driving shift conditions
LambdabarU_dupD = ixp.zerorank2()
lambdaU_dupD = ixp.declarerank2("lambdaU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
LambdabarU_dupD[i][j] = lambdaU_dupD[i][j] * rfm.ReU[i] + lambdaU[i] * rfm.ReUdD[i][j]
global Lambdabar_rhsU # Used on the RHS of the Gamma-driving shift conditions
Lambdabar_rhsU = ixp.zerorank1()
for i in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += betaU[k] * LambdabarU_dupD[i][k] - betaU_dD[i][k] * LambdabarU[k] # Term 1
# Step 6.b: Term 2 of \partial_t \bar{\Lambda}^i = \bar{\gamma}^{jk} (Term 2a + Term 2b + Term 2c)
# Term 2a: \bar{\gamma}^{jk} \beta^i_{,kj}
Term2aUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Term2aUDD[i][j][k] += betaU_dDD[i][k][j]
# Term 2b: \hat{\Gamma}^i_{mk,j} \beta^m + \hat{\Gamma}^i_{mk} \beta^m_{,j}
# + \hat{\Gamma}^i_{dj}\beta^d_{,k} - \hat{\Gamma}^d_{kj} \beta^i_{,d}
Term2bUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
Term2bUDD[i][j][k] += rfm.GammahatUDDdD[i][m][k][j] * betaU[m] \
+ rfm.GammahatUDD[i][m][k] * betaU_dD[m][j] \
+ rfm.GammahatUDD[i][m][j] * betaU_dD[m][k] \
- rfm.GammahatUDD[m][k][j] * betaU_dD[i][m]
# Term 2c: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} \beta^m - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} \beta^m
Term2cUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
for d in range(DIM):
Term2cUDD[i][j][k] += (rfm.GammahatUDD[i][d][j] * rfm.GammahatUDD[d][m][k] \
- rfm.GammahatUDD[d][k][j] * rfm.GammahatUDD[i][m][d]) * betaU[m]
Lambdabar_rhsUpieceU = ixp.zerorank1()
# Put it all together to get Term 2:
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += gammabarUU[j][k] * (Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k])
Lambdabar_rhsUpieceU[i] += gammabarUU[j][k] * (
Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k])
# Step 6.c: Term 3 of \partial_t \bar{\Lambda}^i:
# \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j}
DGammaU = Bq.DGammaU # From Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
for i in range(DIM):
Lambdabar_rhsU[i] += sp.Rational(2, 3) * DGammaU[i] * Dbarbetacontraction # Term 3
# Step 6.d: Term 4 of \partial_t \bar{\Lambda}^i:
# \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j}
detgammabar_dDD = Bq.detgammabar_dDD # From Bq.detgammabar_and_derivs()
Dbarbetacontraction_dBarD = ixp.zerorank1()
for k in range(DIM):
for m in range(DIM):
Dbarbetacontraction_dBarD[m] += betaU_dDD[k][k][m] + \
(betaU_dD[k][m] * detgammabar_dD[k] +
betaU[k] * detgammabar_dDD[k][m]) / (2 * detgammabar) \
- betaU[k] * detgammabar_dD[k] * detgammabar_dD[m] / (
2 * detgammabar * detgammabar)
for i in range(DIM):
for m in range(DIM):
Lambdabar_rhsU[i] += sp.Rational(1, 3) * gammabarUU[i][m] * Dbarbetacontraction_dBarD[m]
# Step 6.e: Term 5 of \partial_t \bar{\Lambda}^i:
# - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \alpha \partial_{j} \phi)
for i in range(DIM):
for j in range(DIM):
Lambdabar_rhsU[i] += -2 * AbarUU[i][j] * (alpha_dD[j] - 6 * alpha * phi_dD[j])
# Step 6.f: Term 6 of \partial_t \bar{\Lambda}^i:
# 2 \alpha \bar{A}^{j k} \Delta^{i}_{j k}
DGammaUDD = Bq.DGammaUDD # From RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += 2 * alpha * AbarUU[j][k] * DGammaUDD[i][j][k]
# Step 6.g: Term 7 of \partial_t \bar{\Lambda}^i:
# -\frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K
trK_dD = ixp.declarerank1("trK_dD")
for i in range(DIM):
for j in range(DIM):
Lambdabar_rhsU[i] += -sp.Rational(4, 3) * alpha * gammabarUU[i][j] * trK_dD[j]
# Step 7: Rescale the RHS quantities so that the evolved
# variables are smooth across coord singularities
global h_rhsDD,a_rhsDD,lambda_rhsU
h_rhsDD = ixp.zerorank2()
a_rhsDD = ixp.zerorank2()
lambda_rhsU = ixp.zerorank1()
for i in range(DIM):
lambda_rhsU[i] = Lambdabar_rhsU[i] / rfm.ReU[i]
for j in range(DIM):
h_rhsDD[i][j] = gammabar_rhsDD[i][j] / rfm.ReDD[i][j]
a_rhsDD[i][j] = Abar_rhsDD[i][j] / rfm.ReDD[i][j]
# 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"))
# print(str(Dbarbetacontraction).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("detgbaroverdetghat","detg"))
# print(betaU_dD)
# 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"))
# 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"))
# This module registers rescaled BSSN T^{mu nu} source term variables
# as AUX (i.e., not evolved) gridfunctions
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: import all needed modules from NRPy+:
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import grid as gri # NRPy+: Functions having to do with numerical grids
def define_BSSN_T4UUmunu_rescaled_source_terms():
# Step 0: First check to see if this function has already been called.
# If so, do not register the gridfunctions again!
for i in range(len(gri.glb_gridfcs_list)):
if "sDD00" in gri.glb_gridfcs_list[i].name:
return
# Step 1: Declare as globals all quantities declared in this function.
global rho,S,sD,sDD
# Step 2: Register all needed *evolved* gridfunctions.
# Step 2a: Register indexed quantities, using ixp.register_... functions
sDD = ixp.register_gridfunctions_for_single_rank2("AUX", "sDD", "sym01")
sD = ixp.register_gridfunctions_for_single_rank1("AUX", "sD")
# Step 2b: Register scalar quantities, using gri.register_gridfunctions()
rho, S = gri.register_gridfunctions("AUX",["rho","S"])
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_constraints.ipynb,
# this module will construct expressions for the
# BSSN Hamiltonian and momentum constraint equations.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Initialize needed Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import BSSN.BSSN_T4UUmunu_vars as BTmunu
thismodule = __name__
def BSSN_constraints(add_T4UUmunu_source_terms=False):
# Step 1.a: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.b: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 2: Hamiltonian constraint.
# First declare all needed variables
Bq.declare_BSSN_gridfunctions_if_not_declared_already() # Sets trK
Bq.BSSN_basic_tensors() # Sets AbarDD
Bq.gammabar__inverse_and_derivs() # Sets gammabarUU
Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() # Sets AbarUU and AbarDD_dD
Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # Sets RbarDD
Bq.phi_and_derivs() # Sets phi_dBarD & phi_dBarDD
###############################
###############################
# HAMILTONIAN CONSTRAINT
###############################
###############################
# Term 1: 2/3 K^2
global H
H = sp.Rational(2, 3) * Bq.trK ** 2
# Term 2: -A_{ij} A^{ij}
for i in range(DIM):
for j in range(DIM):
H += -Bq.AbarDD[i][j] * Bq.AbarUU[i][j]
# Term 3a: trace(Rbar)
Rbartrace = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
Rbartrace += Bq.gammabarUU[i][j] * Bq.RbarDD[i][j]
# Term 3b: -8 \bar{\gamma}^{ij} \bar{D}_i \phi \bar{D}_j \phi = -8*phi_dBar_times_phi_dBar
# Term 3c: -8 \bar{\gamma}^{ij} \bar{D}_i \bar{D}_j \phi = -8*phi_dBarDD_contraction
phi_dBar_times_phi_dBar = sp.sympify(0) # Term 3b
phi_dBarDD_contraction = sp.sympify(0) # Term 3c
for i in range(DIM):
for j in range(DIM):
phi_dBar_times_phi_dBar += Bq.gammabarUU[i][j] * Bq.phi_dBarD[i] * Bq.phi_dBarD[j]
phi_dBarDD_contraction += Bq.gammabarUU[i][j] * Bq.phi_dBarDD[i][j]
# Add Term 3:
H += Bq.exp_m4phi * (Rbartrace - 8 * (phi_dBar_times_phi_dBar + phi_dBarDD_contraction))
if add_T4UUmunu_source_terms:
M_PI = par.Cparameters("#define", thismodule, "M_PI","") # M_PI is pi as defined in C
BTmunu.define_BSSN_T4UUmunu_rescaled_source_terms()
rho = BTmunu.rho
H += -16*M_PI*rho
# FIXME: ADD T4UUmunu SOURCE TERMS TO MOMENTUM CONSTRAINT!
# Step 3: M^i, the momentum constraint
###############################
###############################
# MOMENTUM CONSTRAINT
###############################
###############################
# SEE Tutorial-BSSN_constraints.ipynb for full documentation.
global MU
MU = ixp.zerorank1()
# Term 2: 6 A^{ij} \partial_j \phi:
for i in range(DIM):
for j in range(DIM):
MU[i] += 6*Bq.AbarUU[i][j]*Bq.phi_dD[j]
# Term 3: -2/3 \bar{\gamma}^{ij} K_{,j}
trK_dD = ixp.declarerank1("trK_dD") # Not defined in BSSN_RHSs; only trK_dupD is defined there.
for i in range(DIM):
for j in range(DIM):
MU[i] += -sp.Rational(2,3)*Bq.gammabarUU[i][j]*trK_dD[j]
# Next evaluate the conformal covariant derivative \bar{D}_j \bar{A}_{lm}
AbarDD_dBarD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
AbarDD_dBarD[i][j][k] = Bq.AbarDD_dD[i][j][k]
for l in range(DIM):
AbarDD_dBarD[i][j][k] += -Bq.GammabarUDD[l][k][i] * Bq.AbarDD[l][j]
AbarDD_dBarD[i][j][k] += -Bq.GammabarUDD[l][k][j] * Bq.AbarDD[i][l]
# Term 1: Contract twice with the metric to make \bar{D}_{j} \bar{A}^{ij}
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
MU[i] += Bq.gammabarUU[i][k] * Bq.gammabarUU[j][l] * AbarDD_dBarD[k][l][j]
# Finally, we multiply by e^{-4 phi} and rescale the momentum constraint:
for i in range(DIM):
MU[i] *= Bq.exp_m4phi / rfm.ReU[i]
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.ipynb
# this module will construct the right-hand sides (RHSs)
# expressions for the time evolution equations of the
# BSSN gauge quantities alpha and beta^i (i.e., the
# lapse and shift)
#
# Non-gauge BSSN time-evolution equations are documented in the
# NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_RHSs.ipynb,
# and separately constructed in the BSSN.BSSN_RHSs
# Python module.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import all needed modules from NRPy+:
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import BSSN.BSSN_RHSs as Brhs # NRPy+: Constructs BSSN right-hand-side expressions
import sys # Standard Python modules for multiplatform OS-level functions
# Step 1.a: Declare/initialize parameters for this module
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "LapseEvolutionOption", "OnePlusLog"))
par.initialize_param(par.glb_param("char", thismodule, "ShiftEvolutionOption", "GammaDriving2ndOrder_Covariant"))
def BSSN_gauge_RHSs():
# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.e: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 1.f: Define needed BSSN quantities:
# Declare scalars & tensors (in terms of rescaled BSSN quantities)
Bq.BSSN_basic_tensors()
Bq.betaU_derivs()
# Declare BSSN_RHSs (excluding the time evolution equations for the gauge conditions),
# if they haven't already been declared.
if Brhs.have_already_called_BSSN_RHSs_function == False:
print("BSSN_gauge_RHSs() Error: You must call BSSN_RHSs() before calling BSSN_gauge_RHSs().")
sys.exit(1)
# Step 2: Lapse conditions
LapseEvolOption = par.parval_from_str(thismodule + "::LapseEvolutionOption")
# Step 2.a: The 1+log lapse condition:
# \partial_t \alpha = \beta^i \alpha_{,i} - 2*\alpha*K
# First import expressions from BSSN_quantities
cf = Bq.cf
trK = Bq.trK
alpha = Bq.alpha
betaU = Bq.betaU
# Implement the 1+log lapse condition
global alpha_rhs
alpha_rhs = sp.sympify(0)
if LapseEvolOption == "OnePlusLog":
alpha_rhs = -2 * alpha * trK
alpha_dupD = ixp.declarerank1("alpha_dupD")
for i in range(DIM):
alpha_rhs += betaU[i] * alpha_dupD[i]
# Step 2.b: Implement the harmonic slicing lapse condition
elif LapseEvolOption == "HarmonicSlicing":
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
alpha_rhs = -3 * cf ** (-4) * Brhs.cf_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
alpha_rhs = 6 * sp.exp(6 * cf) * Brhs.cf_rhs
else:
print("Error LapseEvolutionOption==HarmonicSlicing unsupported for EvolvedConformalFactor_cf!=(W or phi)")
sys.exit(1)
# Step 2.c: Frozen lapse
# \partial_t \alpha = 0
elif LapseEvolOption == "Frozen":
alpha_rhs = sp.sympify(0)
else:
print("Error: "+thismodule + "::LapseEvolutionOption == "+LapseEvolOption+" not supported!")
sys.exit(1)
# Step 3.a: Set \partial_t \beta^i
# First check that ShiftEvolutionOption parameter choice is supported.
ShiftEvolOption = par.parval_from_str(thismodule + "::ShiftEvolutionOption")
if ShiftEvolOption != "Frozen" and \
ShiftEvolOption != "GammaDriving2ndOrder_NoCovariant" and \
ShiftEvolOption != "GammaDriving2ndOrder_Covariant" and \
ShiftEvolOption != "GammaDriving2ndOrder_Covariant__Hatted" and \
ShiftEvolOption != "GammaDriving1stOrder_Covariant" and \
ShiftEvolOption != "GammaDriving1stOrder_Covariant__Hatted":
print("Error: ShiftEvolutionOption == " + ShiftEvolOption + " unsupported!")
sys.exit(1)
# Next import expressions from BSSN_quantities
BU = Bq.BU
betU = Bq.betU
betaU_dupD = Bq.betaU_dupD
# Define needed quantities
beta_rhsU = ixp.zerorank1()
B_rhsU = ixp.zerorank1()
# In the case of Frozen shift condition, we
# explicitly set the betaU and BU RHS's to zero
# instead of relying on the ixp.zerorank1()'s above,
# for safety.
if ShiftEvolOption == "Frozen":
for i in range(DIM):
beta_rhsU[i] = sp.sympify(0)
BU[i] = sp.sympify(0)
if ShiftEvolOption == "GammaDriving2ndOrder_NoCovariant":
# Step 3.a.i: Compute right-hand side of beta^i
# * \partial_t \beta^i = \beta^j \beta^i_{,j} + B^i
for i in range(DIM):
beta_rhsU[i] += BU[i]
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Compute right-hand side of B^i:
eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)
# Step 3.a.ii: Compute right-hand side of B^i
# * \partial_t B^i = \beta^j B^i_{,j} + 3/4 * \partial_0 \Lambda^i - eta B^i
# Step 3.a.iii: Define BU_dupD, in terms of derivative of rescaled variable \bet^i
BU_dupD = ixp.zerorank2()
betU_dupD = ixp.declarerank2("betU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[i] * rfm.ReUdD[i][j]
# Step 3.a.iv: Compute \partial_0 \bar{\Lambda}^i = (\partial_t - \beta^i \partial_i) \bar{\Lambda}^j
Lambdabar_partial0 = ixp.zerorank1()
for i in range(DIM):
Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i]
for i in range(DIM):
for j in range(DIM):
Lambdabar_partial0[j] += -betaU[i] * Brhs.LambdabarU_dupD[j][i]
# Step 3.a.v: Evaluate RHS of B^i:
for i in range(DIM):
B_rhsU[i] += sp.Rational(3, 4) * Lambdabar_partial0[i] - eta * BU[i]
for j in range(DIM):
B_rhsU[i] += betaU[j] * BU_dupD[i][j]
# Step 3.b: The right-hand side of the \partial_t \beta^i equation
if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption:
# Step 3.b Option 2: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i}
# First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()
Bq.gammabar__inverse_and_derivs()
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Then compute right-hand side:
# Term 1: \beta^j \beta^i_{,j}
for i in range(DIM):
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
beta_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m]
# Term 3: B^i
for i in range(DIM):
beta_rhsU[i] += BU[i]
if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption:
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Step 3.c: Covariant option:
# \partial_t B^i = \beta^j \bar{D}_j B^i
# + \frac{3}{4} ( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} )
# - \eta B^{i}
# = \beta^j B^i_{,j} + \beta^j \bar{\Gamma}^i_{mj} B^m
# + \frac{3}{4}[ \partial_t \bar{\Lambda}^{i}
# - \beta^j (\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m)]
# - \eta B^{i}
# Term 1, part a: First compute B^i_{,j} using upwinded derivative
BU_dupD = ixp.zerorank2()
betU_dupD = ixp.declarerank2("betU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[i] * rfm.ReUdD[i][j]
# Term 1: \beta^j B^i_{,j}
for i in range(DIM):
for j in range(DIM):
B_rhsU[i] += betaU[j] * BU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} B^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
B_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * BU[m]
# Term 3: \frac{3}{4}\partial_t \bar{\Lambda}^{i}
for i in range(DIM):
B_rhsU[i] += sp.Rational(3, 4) * Brhs.Lambdabar_rhsU[i]
# Term 4: -\frac{3}{4}\beta^j \bar{\Lambda}^i_{,j}
for i in range(DIM):
for j in range(DIM):
B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * Brhs.LambdabarU_dupD[i][j]
# Term 5: -\frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * ConnectionUDD[i][m][j] * Bq.LambdabarU[m]
# Term 6: - \eta B^i
# eta is a free parameter; we declare it here:
eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)
for i in range(DIM):
B_rhsU[i] += -eta * BU[i]
if "GammaDriving1stOrder_Covariant" in ShiftEvolOption:
# Step 3.c: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + 3/4 Lambdabar^i - eta*beta^i
# First set \partial_t B^i = 0:
B_rhsU = ixp.zerorank1() # \partial_t B^i = 0
# Second, set \partial_t beta^i RHS:
# Compute covariant advection term:
# We need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()
Bq.gammabar__inverse_and_derivs()
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving1stOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Term 1: \beta^j \beta^i_{,j}
for i in range(DIM):
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
beta_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m]
# Term 3: 3/4 Lambdabar^i - eta*beta^i
eta = par.Cparameters("REAL", thismodule, ["eta"], 2.0)
for i in range(DIM):
beta_rhsU[i] += sp.Rational(3, 4) * Bq.LambdabarU[i] - eta * betaU[i]
# Step 4: Rescale the BSSN gauge RHS quantities so that the evolved
# variables may remain smooth across coord singularities
global vet_rhsU,bet_rhsU
vet_rhsU = ixp.zerorank1()
bet_rhsU = ixp.zerorank1()
for i in range(DIM):
vet_rhsU[i] = beta_rhsU[i] / rfm.ReU[i]
bet_rhsU[i] = B_rhsU[i] / rfm.ReU[i]
# 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"))
# print(str(Dbarbetacontraction).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("detgbaroverdetghat","detg"))
# print(betaU_dD)
# 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"))
# 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"))
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_in_terms_of_ADM.ipynb,
# this module will construct expressions for BSSN
# quantities in terms of ADM quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import needed core NRPy+ modules
from outputC import * # NRPy+: Core C code output module
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
import BSSN.BSSN_quantities as Bq # NRPy+: This module depends on the parameter EvolvedConformalFactor_cf,
# which is defined in BSSN.BSSN_quantities
# Step 1.a: Set DIM=3, as we're using a 3+1 decomposition of Einstein's equations
DIM=3
# Step 2: All ADM quantities were input into this function in the Spherical or Cartesian
# basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
# we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
# Here we convert ADM quantities to their BSSN Curvilinear counterparts:
# Step 2.a: Convert ADM $\gamma_{ij}$ to BSSN $\bar{gamma}_{ij}$:
# We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
def gammabarDD_hDD(gammaDD):
global gammabarDD,hDD
if gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.hDD_given_ADM(): Must call reference_metric() first!")
sys.exit(1)
# \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
gammabarDD = ixp.zerorank2()
hDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
gammabarDD[i][j] = (rfm.detgammahat/gammaDET)**(sp.Rational(1,3))*gammaDD[i][j]
hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
# Step 2.b: Convert the extrinsic curvature K_{ij} to the trace-free extrinsic
# curvature \bar{A}_{ij}, plus the trace of the extrinsic curvature K,
# where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):
def trK_AbarDD_aDD(gammaDD,KDD):
global trK,AbarDD,aDD
if gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
if KDD == None:
KDD = ixp.declarerank2("KDD","sym01")
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.trK_AbarDD(): Must call reference_metric() first!")
sys.exit(1)
# \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
# K = gamma^{ij} K_{ij}, and
# \bar{A}_{ij} &= (\frac{\bar{gamma}}{gamma})^{1/3}*(K_{ij} - \frac{1}{3}*gamma_{ij}*K)
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j]*KDD[i][j]
AbarDD = ixp.zerorank2()
aDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
AbarDD[i][j] = (rfm.detgammahat/gammaDET)**(sp.Rational(1,3))*(KDD[i][j] - sp.Rational(1,3)*gammaDD[i][j]*trK)
aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
# Step 2.c: Define \bar{Lambda}^i (Eqs. 4 and 5 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):
def LambdabarU_lambdaU__exact_gammaDD(gammaDD):
global LambdabarU, lambdaU
if gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
# \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}).
gammabarDD_hDD(gammaDD)
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
# First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric:
GammabarUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammabarUDD[i][j][k] += sp.Rational(1, 2) * gammabarUU[i][l] * (
sp.diff(gammabarDD[l][j], rfm.xx[k]) +
sp.diff(gammabarDD[l][k], rfm.xx[j]) -
sp.diff(gammabarDD[j][k], rfm.xx[l]))
# Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD
# (from the reference metric):
LambdabarU = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k])
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
# Step 2.d: Set the conformal factor variable cf, which is set
# by the "BSSN_quantities::EvolvedConformalFactor_cf" parameter. For example if
# "EvolvedConformalFactor_cf" is set to "phi", we can use Eq. 3 of
# [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf),
# which in arbitrary coordinates is written:
def cf_from_gammaDD(gammaDD):
global cf
if gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
# \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}).
gammabarDD_hDD(gammaDD)
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
cf = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
# phi = \frac{1}{12} log(\frac{gamma}{\bar{gamma}}).
cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
# chi = exp(-4*phi) = exp(-4*\frac{1}{12}*(\frac{gamma}{\bar{gamma}}))
# = exp(-\frac{1}{3}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{\bar{gamma}})^{-1/3}.
#
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 3))
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
# W = exp(-2*phi) = exp(-2*\frac{1}{12}*log(\frac{gamma}{\bar{gamma}}))
# = exp(-\frac{1}{6}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{bar{gamma}})^{-1/6}.
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 6))
else:
print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str(
"EvolvedConformalFactor_cf") + "\" unknown.")
sys.exit(1)
# Step 2.e: Rescale beta^i and B^i according to the prescription described in
# the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb)
# (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
#
# \mathcal{V}^i &= beta^i/(ReU[i])
# \mathcal{B}^i &= B^i/(ReU[i])
def betU_vetU(betaU,BU):
global vetU,betU
if betaU == None:
betaU = ixp.declarerank1("betaU")
if BU == None:
BU = ixp.declarerank1("BU")
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.bet_vet(): Must call reference_metric() first!")
sys.exit(1)
vetU = ixp.zerorank1()
betU = ixp.zerorank1()
for i in range(DIM):
vetU[i] = betaU[i] / rfm.ReU[i]
betU[i] = BU[i] / rfm.ReU[i]
# This module provides functions that declare and define useful BSSN quantities
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import all needed modules from NRPy+:
import NRPy_param_funcs as par # NRPy+: Parameter interface
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import grid as gri # NRPy+: Functions having to do with numerical grids
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
# Step 1.a: Set the coordinate system for the numerical grid
# DO NOT SET IN STANDALONE PYTHON MODULE
# par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
# Step 1.b: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
# DO NOT CALL IN STANDALONE PYTHON MODULE
# rfm.reference_metric()
# Step 1.c: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
# DO NOT CALL IN STANDALONE PYTHON MODULE
# DIM = 3
# par.set_parval_from_str("grid::DIM",DIM)
# Step 1.d: Declare/initialize parameters for this module
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "EvolvedConformalFactor_cf", "W"))
par.initialize_param(par.glb_param("bool", thismodule, "detgbarOverdetghat_equals_one", "True"))
par.initialize_param(par.glb_param("bool", thismodule, "LeaveRicciSymbolic", "False"))
def declare_BSSN_gridfunctions_if_not_declared_already():
# Step 2: Register all needed BSSN gridfunctions.
# Declare as globals all variables that may be
# used outside this function
global hDD,aDD,lambdaU,vetU,betU,trK,cf,alpha
# Check to see if this function has already been called.
# If so, do not register the gridfunctions again!
for i in range(len(gri.glb_gridfcs_list)):
if "hDD00" in gri.glb_gridfcs_list[i].name:
hDD = ixp.declarerank2("hDD", "sym01")
aDD = ixp.declarerank2("aDD", "sym01")
lambdaU = ixp.declarerank1("lambdaU")
vetU = ixp.declarerank1("vetU")
betU = ixp.declarerank1("betU")
trK, cf, alpha = sp.symbols('trK cf alpha', real=True)
return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
# Step 2.a: Register indexed quantities, using ixp.register_... functions
hDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "hDD", "sym01")
aDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "aDD", "sym01")
lambdaU = ixp.register_gridfunctions_for_single_rank1("EVOL", "lambdaU")
vetU = ixp.register_gridfunctions_for_single_rank1("EVOL", "vetU")
betU = ixp.register_gridfunctions_for_single_rank1("EVOL", "betU")
# Step 2.b: Register scalar quantities, using gri.register_gridfunctions()
trK, cf, alpha = gri.register_gridfunctions("EVOL", ["trK", "cf", "alpha"])
return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
# Step 3: Define all basic conformal BSSN tensors
# gammabarDD,AbarDD,LambdabarU,betaU,BU
# in terms of BSSN gridfunctions.
def BSSN_basic_tensors():
# Step 3.a: Declare as globals all variables that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global gammabarDD,AbarDD,LambdabarU,betaU,BU
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# Step 3.a.i: gammabarDD and AbarDD:
gammabarDD = ixp.zerorank2()
AbarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
# gammabar_{ij} = h_{ij}*ReDD[i][j] + gammahat_{ij}
gammabarDD[i][j] = hDD[i][j] * rfm.ReDD[i][j] + rfm.ghatDD[i][j]
# Abar_{ij} = a_{ij}*ReDD[i][j]
AbarDD[i][j] = aDD[i][j] * rfm.ReDD[i][j]
# Step 3.a.ii: LambdabarU, betaU, and BU:
LambdabarU = ixp.zerorank1()
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
for i in range(DIM):
LambdabarU[i] = lambdaU[i] * rfm.ReU[i]
betaU[i] = vetU[i] * rfm.ReU[i]
BU[i] = betU[i] * rfm.ReU[i]
# Step 4: gammabarUU and spatial derivatives of gammabarDD,
# including GammabarUDD
def gammabar__inverse_and_derivs():
# Step 4.a: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global gammabarUU, gammabarDD_dD, gammabarDD_dupD, gammabarDD_dDD, GammabarUDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# This function needs gammabarDD, defined in BSSN_basic_tensors()
BSSN_basic_tensors()
# Step 4.a.i: gammabarUU:
gammabarUU, dummydet = ixp.symm_matrix_inverter3x3(gammabarDD)
# Step 4.b.i: gammabarDDdD[i][j][k]
# = \hat{\gamma}_{ij,k} + h_{ij,k} \text{ReDD[i][j]} + h_{ij} \text{ReDDdD[i][j][k]}.
gammabarDD_dD = ixp.zerorank3()
gammabarDD_dupD = ixp.zerorank3()
hDD_dD = ixp.declarerank3("hDD_dD", "sym01")
hDD_dupD = ixp.declarerank3("hDD_dupD", "sym01")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammabarDD_dD[i][j][k] = rfm.ghatDDdD[i][j][k] + \
hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Compute associated upwinded derivative, needed for the \bar{\gamma}_{ij} RHS
gammabarDD_dupD[i][j][k] = rfm.ghatDDdD[i][j][k] + \
hDD_dupD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Step 4.b.ii: Compute gammabarDD_dDD in terms of the rescaled BSSN quantity hDD
# and its derivatives, as well as the reference metric and rescaling
# matrix, and its derivatives (expression given below):
hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23")
gammabarDD_dDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
# gammabar_{ij,kl} = gammahat_{ij,kl}
# + h_{ij,kl} ReDD[i][j]
# + h_{ij,k} ReDDdD[i][j][l] + h_{ij,l} ReDDdD[i][j][k]
# + h_{ij} ReDDdDD[i][j][k][l]
gammabarDD_dDD[i][j][k][l] = rfm.ghatDDdDD[i][j][k][l]
gammabarDD_dDD[i][j][k][l] += hDD_dDD[i][j][k][l] * rfm.ReDD[i][j]
gammabarDD_dDD[i][j][k][l] += hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \
hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k]
gammabarDD_dDD[i][j][k][l] += hDD[i][j] * rfm.ReDDdDD[i][j][k][l]
# Step 4.b.iii: Define barred Christoffel symbol \bar{\Gamma}^{i}_{kl} = GammabarUDD[i][k][l] (see expression below)
GammabarUDD = ixp.zerorank3()
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
# Gammabar^i_{kl} = 1/2 * gammabar^{im} ( gammabar_{mk,l} + gammabar_{ml,k} - gammabar_{kl,m}):
GammabarUDD[i][k][l] += sp.Rational(1, 2) * gammabarUU[i][m] * \
(gammabarDD_dD[m][k][l] + gammabarDD_dD[m][l][k] - gammabarDD_dD[k][l][m])
# Step 5: det(gammabarDD) and its derivatives
def detgammabar_and_derivs():
# Step 5.a: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global detgammabar,detgammabar_dD,detgammabar_dDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
detgbarOverdetghat = sp.sympify(1)
detgbarOverdetghat_dD = ixp.zerorank1()
detgbarOverdetghat_dDD = ixp.zerorank2()
if par.parval_from_str(thismodule + "::detgbarOverdetghat_equals_one") == "False":
print("Error: detgbarOverdetghat_equals_one=\"False\" is not fully implemented yet.")
sys.exit(1)
## Approach for implementing detgbarOverdetghat_equals_one=False:
# detgbarOverdetghat = gri.register_gridfunctions("AUX", ["detgbarOverdetghat"])
# detgbarOverdetghatInitial = gri.register_gridfunctions("AUX", ["detgbarOverdetghatInitial"])
# detgbarOverdetghat_dD = ixp.declarerank1("detgbarOverdetghat_dD")
# detgbarOverdetghat_dDD = ixp.declarerank2("detgbarOverdetghat_dDD", "sym01")
# Step 5.b: Define detgammabar, detgammabar_dD, and detgammabar_dDD (needed for \partial_t \bar{\Lambda}^i below)
detgammabar = detgbarOverdetghat * rfm.detgammahat
detgammabar_dD = ixp.zerorank1()
for i in range(DIM):
detgammabar_dD[i] = detgbarOverdetghat_dD[i] * rfm.detgammahat + detgbarOverdetghat * rfm.detgammahatdD[i]
detgammabar_dDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
detgammabar_dDD[i][j] = detgbarOverdetghat_dDD[i][j] * rfm.detgammahat + \
detgbarOverdetghat_dD[i] * rfm.detgammahatdD[j] + \
detgbarOverdetghat_dD[j] * rfm.detgammahatdD[i] + \
detgbarOverdetghat * rfm.detgammahatdDD[i][j]
# Step 6: Quantities related to conformal traceless
# extrinsic curvature AbarDD:
# AbarUU, AbarUD, and trAbar
def AbarUU_AbarUD_trAbar_AbarDD_dD():
# Step 6.a: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global AbarUU,AbarUD,trAbar,AbarDD_dD,AbarDD_dupD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# Define AbarDD and gammabarDD in terms of BSSN gridfunctions
BSSN_basic_tensors()
# Define gammabarUU in terms of BSSN gridfunctions
gammabar__inverse_and_derivs()
# Step 6.a.i: Compute Abar^{ij} in terms of Abar_{ij} and gammabar^{ij}
AbarUU = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
# Abar^{ij} = gammabar^{ik} gammabar^{jl} Abar_{kl}
AbarUU[i][j] += gammabarUU[i][k] * gammabarUU[j][l] * AbarDD[k][l]
# Step 6.a.ii: Compute Abar^i_j in terms of Abar_{ij} and gammabar^{ij}
AbarUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# Abar^i_j = gammabar^{ik} Abar_{kj}
AbarUD[i][j] += gammabarUU[i][k] * AbarDD[k][j]
# Step 6.a.iii: Compute Abar^k_k = trace of Abar:
trAbar = sp.sympify(0)
for k in range(DIM):
for j in range(DIM):
# Abar^k_k = gammabar^{kj} Abar_{jk}
trAbar += gammabarUU[k][j] * AbarDD[j][k]
# Step 6.a.iv: Compute Abar_{ij,k}
AbarDD_dD = ixp.zerorank3()
AbarDD_dupD = ixp.zerorank3()
aDD_dD = ixp.declarerank3("aDD_dD" ,"sym01")
aDD_dupD = ixp.declarerank3("aDD_dupD","sym01")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
AbarDD_dupD[i][j][k] = rfm.ReDDdD[i][j][k]*aDD[i][j] + rfm.ReDD[i][j]*aDD_dupD[i][j][k]
AbarDD_dD[i][j][k] = rfm.ReDDdD[i][j][k]*aDD[i][j] + rfm.ReDD[i][j]*aDD_dD[ i][j][k]
# Step 7: The conformal ("barred") Ricci tensor RbarDD
# and associated quantities
def RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU():
# Step 7.a: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global RbarDD,DGammaUDD,gammabarDD_dHatD,DGammaU
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# GammabarUDD is used below, defined in
# gammabar__inverse_and_derivs()
gammabar__inverse_and_derivs()
# Step 7.a.i: Define \varepsilon_{ij} = epsDD[i][j]
epsDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
epsDD[i][j] = hDD[i][j] * rfm.ReDD[i][j]
# Step 7.a.ii: Define epsDD_dD[i][j][k]
hDD_dD = ixp.declarerank3("hDD_dD", "sym01")
epsDD_dD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
epsDD_dD[i][j][k] = hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Step 7.a.iii: Define epsDD_dDD[i][j][k][l]
hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23")
epsDD_dDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
epsDD_dDD[i][j][k][l] = hDD_dDD[i][j][k][l] * rfm.ReDD[i][j] + \
hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \
hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k] + \
hDD[i][j] * rfm.ReDDdDD[i][j][k][l]
# Step 7.a.iv: DhatgammabarDDdD[i][j][l] = \bar{\gamma}_{ij;\hat{l}}
# \bar{\gamma}_{ij;\hat{l}} = \varepsilon_{i j,l}
# - \hat{\Gamma}^m_{i l} \varepsilon_{m j}
# - \hat{\Gamma}^m_{j l} \varepsilon_{i m}
gammabarDD_dHatD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
gammabarDD_dHatD[i][j][l] = epsDD_dD[i][j][l]
for m in range(DIM):
gammabarDD_dHatD[i][j][l] += - rfm.GammahatUDD[m][i][l] * epsDD[m][j] \
- rfm.GammahatUDD[m][j][l] * epsDD[i][m]
# Step 7.a.v: \bar{\gamma}_{ij;\hat{l},k} = DhatgammabarDD_dHatD_dD[i][j][l][k]:
# \bar{\gamma}_{ij;\hat{l},k} = \varepsilon_{ij,lk}
# - \hat{\Gamma}^m_{i l,k} \varepsilon_{m j}
# - \hat{\Gamma}^m_{i l} \varepsilon_{m j,k}
# - \hat{\Gamma}^m_{j l,k} \varepsilon_{i m}
# - \hat{\Gamma}^m_{j l} \varepsilon_{i m,k}
gammabarDD_dHatD_dD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
for k in range(DIM):
gammabarDD_dHatD_dD[i][j][l][k] = epsDD_dDD[i][j][l][k]
for m in range(DIM):
gammabarDD_dHatD_dD[i][j][l][k] += -rfm.GammahatUDDdD[m][i][l][k] * epsDD[m][j] \
- rfm.GammahatUDD[m][i][l] * epsDD_dD[m][j][k] \
- rfm.GammahatUDDdD[m][j][l][k] * epsDD[i][m] \
- rfm.GammahatUDD[m][j][l] * epsDD_dD[i][m][k]
# Step 7.a.vi: \bar{\gamma}_{ij;\hat{l}\hat{k}} = DhatgammabarDD_dHatDD[i][j][l][k]
# \bar{\gamma}_{ij;\hat{l}\hat{k}} = \partial_k \hat{D}_{l} \varepsilon_{i j}
# - \hat{\Gamma}^m_{lk} \left(\hat{D}_{m} \varepsilon_{i j}\right)
# - \hat{\Gamma}^m_{ik} \left(\hat{D}_{l} \varepsilon_{m j}\right)
# - \hat{\Gamma}^m_{jk} \left(\hat{D}_{l} \varepsilon_{i m}\right)
gammabarDD_dHatDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
for k in range(DIM):
gammabarDD_dHatDD[i][j][l][k] = gammabarDD_dHatD_dD[i][j][l][k]
for m in range(DIM):
gammabarDD_dHatDD[i][j][l][k] += - rfm.GammahatUDD[m][l][k] * gammabarDD_dHatD[i][j][m] \
- rfm.GammahatUDD[m][i][k] * gammabarDD_dHatD[m][j][l] \
- rfm.GammahatUDD[m][j][k] * gammabarDD_dHatD[i][m][l]
# Step 7.b: Second term of RhatDD: compute \hat{D}_{j} \bar{\Lambda}^{k} = LambarU_dHatD[k][j]
lambdaU_dD = ixp.declarerank2("lambdaU_dD", "nosym")
LambarU_dHatD = ixp.zerorank2()
for j in range(DIM):
for k in range(DIM):
LambarU_dHatD[k][j] = lambdaU_dD[k][j] * rfm.ReU[k] + lambdaU[k] * rfm.ReUdD[k][j]
for m in range(DIM):
LambarU_dHatD[k][j] += rfm.GammahatUDD[k][m][j] * lambdaU[m] * rfm.ReU[m]
# Step 7.c: Conformal Ricci tensor, part 3: The \Delta^{k} \Delta_{(i j) k}
# + \bar{\gamma}^{k l}*(2 \Delta_{k(i}^{m} \Delta_{j) m l}
# + \Delta_{i k}^{m} \Delta_{m j l}) terms
# Step 7.c.i: Define \Delta^i_{jk} = \bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk} = DGammaUDD[i][j][k]
DGammaUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
DGammaUDD[i][j][k] = GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]
# Step 7.c.ii: Define \Delta^i = \bar{\gamma}^{jk} \Delta^i_{jk}
DGammaU = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
DGammaU[i] += gammabarUU[j][k] * DGammaUDD[i][j][k]
# Step 7.c.iii: Define \Delta_{ijk} = \bar{\gamma}_{im} \Delta^m_{jk}
DGammaDDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
DGammaDDD[i][j][k] += gammabarDD[i][m] * DGammaUDD[m][j][k]
if par.parval_from_str(thismodule+"::LeaveRicciSymbolic") == "True":
for i in range(len(gri.glb_gridfcs_list)):
if "RbarDD00" in gri.glb_gridfcs_list[i].name:
return
RbarDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","RbarDD","sym01")
return
# Step 7.d: Summing the terms and defining \bar{R}_{ij}
# Step 7.d.i: Add the first term to RbarDD:
# Rbar_{ij} += - \frac{1}{2} \bar{\gamma}^{k l} \hat{D}_{k} \hat{D}_{l} \bar{\gamma}_{i j}
RbarDD = ixp.zerorank2()
RbarDDpiece = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
RbarDD[i][j] += -sp.Rational(1, 2) * gammabarUU[k][l] * gammabarDD_dHatDD[i][j][l][k]
RbarDDpiece[i][j] += -sp.Rational(1, 2) * gammabarUU[k][l] * gammabarDD_dHatDD[i][j][l][k]
# Step 7.d.ii: Add the second term to RbarDD:
# Rbar_{ij} += (1/2) * (gammabar_{ki} Lambar^k_{;\hat{j}} + gammabar_{kj} Lambar^k_{;\hat{i}})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
RbarDD[i][j] += sp.Rational(1, 2) * ( gammabarDD[k][i] * LambarU_dHatD[k][j] +
gammabarDD[k][j] * LambarU_dHatD[k][i] )
# Step 7.d.iii: Add the remaining term to RbarDD:
# Rbar_{ij} += \Delta^{k} \Delta_{(i j) k} = 1/2 \Delta^{k} (\Delta_{i j k} + \Delta_{j i k})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
RbarDD[i][j] += sp.Rational(1, 2) * DGammaU[k] * (DGammaDDD[i][j][k] + DGammaDDD[j][i][k])
# Step 7.d.iv: Add the final term to RbarDD:
# Rbar_{ij} += \bar{\gamma}^{k l} (\Delta^{m}_{k i} \Delta_{j m l}
# + \Delta^{m}_{k j} \Delta_{i m l}
# + \Delta^{m}_{i k} \Delta_{m j l})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
RbarDD[i][j] += gammabarUU[k][l] * (DGammaUDD[m][k][i] * DGammaDDD[j][m][l] +
DGammaUDD[m][k][j] * DGammaDDD[i][m][l] +
DGammaUDD[m][i][k] * DGammaDDD[m][j][l])
# Step 8: The unrescaled shift vector betaU spatial derivatives:
# betaUdD & betaUdDD, written in terms of the
# rescaled shift vector vetU
def betaU_derivs():
# Step 8.i: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global betaU_dD,betaU_dupD,betaU_dDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# Step 8.ii: Compute the unrescaled shift vector beta^i = ReU[i]*vet^i
vetU_dD = ixp.declarerank2("vetU_dD", "nosym")
vetU_dupD = ixp.declarerank2("vetU_dupD", "nosym") # Needed for upwinded \beta^i_{,j}
vetU_dDD = ixp.declarerank3("vetU_dDD", "sym12") # Needed for \beta^i_{,j}
betaU_dD = ixp.zerorank2()
betaU_dupD = ixp.zerorank2() # Needed for, e.g., \beta^i RHS
betaU_dDD = ixp.zerorank3() # Needed for, e.g., \bar{\Lambda}^i RHS
for i in range(DIM):
for j in range(DIM):
betaU_dD[i][j] = vetU_dD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j]
betaU_dupD[i][j] = vetU_dupD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j] # Needed for \beta^i RHS
for k in range(DIM):
# Needed for, e.g., \bar{\Lambda}^i RHS:
betaU_dDD[i][j][k] = vetU_dDD[i][j][k] * rfm.ReU[i] + vetU_dD[i][j] * rfm.ReUdD[i][k] + \
vetU_dD[i][k] * rfm.ReUdD[i][j] + vetU[i] * rfm.ReUdDD[i][j][k]
# Step 9: Standard BSSN conformal factor phi,
# and its partial and covariant derivatives,
# all in terms of BSSN gridfunctions like cf
def phi_and_derivs():
# Step 9.a: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global phi_dD,phi_dupD,phi_dDD,exp_m4phi,phi_dBarD,phi_dBarDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# GammabarUDD is used below, defined in
# gammabar__inverse_and_derivs()
gammabar__inverse_and_derivs()
# Step 9.a.i: Define partial derivatives of \phi in terms of evolved quantity "cf":
cf_dD = ixp.declarerank1("cf_dD")
cf_dupD = ixp.declarerank1("cf_dupD") # Needed for \partial_t \phi next.
cf_dDD = ixp.declarerank2("cf_dDD", "sym01")
phi_dD = ixp.zerorank1()
phi_dupD = ixp.zerorank1()
phi_dDD = ixp.zerorank2()
exp_m4phi = sp.sympify(0)
# Step 9.a.ii: Assuming cf=phi, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
for i in range(DIM):
phi_dD[i] = cf_dD[i]
phi_dupD[i] = cf_dupD[i]
for j in range(DIM):
phi_dDD[i][j] = cf_dDD[i][j]
exp_m4phi = sp.exp(-4 * cf)
# Step 9.a.iii: Assuming cf=W=e^{-2 phi}, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
# \partial_i W = \partial_i (e^{-2 phi}) = -2 e^{-2 phi} \partial_i phi
# -> \partial_i phi = -\partial_i cf / (2 cf)
for i in range(DIM):
phi_dD[i] = - cf_dD[i] / (2 * cf)
phi_dupD[i] = - cf_dupD[i] / (2 * cf)
for j in range(DIM):
# \partial_j \partial_i phi = - \partial_j [\partial_i cf / (2 cf)]
# = - cf_{,ij} / (2 cf) + \partial_i cf \partial_j cf / (2 cf^2)
phi_dDD[i][j] = (- cf_dDD[i][j] + cf_dD[i] * cf_dD[j] / cf) / (2 * cf)
exp_m4phi = cf * cf
# Step 9.a.iv: Assuming cf=chi=e^{-4 phi}, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi":
# \partial_i chi = \partial_i (e^{-4 phi}) = -4 e^{-4 phi} \partial_i phi
# -> \partial_i phi = -\partial_i cf / (4 cf)
for i in range(DIM):
phi_dD[i] = - cf_dD[i] / (4 * cf)
phi_dupD[i] = - cf_dupD[i] / (4 * cf)
for j in range(DIM):
# \partial_j \partial_i phi = - \partial_j [\partial_i cf / (4 cf)]
# = - cf_{,ij} / (4 cf) + \partial_i cf \partial_j cf / (4 cf^2)
phi_dDD[i][j] = (- cf_dDD[i][j] + cf_dD[i] * cf_dD[j] / cf) / (4 * cf)
exp_m4phi = cf
# Step 9.a.v: Error out if unsupported EvolvedConformalFactor_cf choice is made:
cf_choice = par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf")
if not (cf_choice == "phi" or cf_choice == "W" or cf_choice == "chi"):
print("Error: EvolvedConformalFactor_cf == " + par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") + " unsupported!")
sys.exit(1)
# Step 9.b: Define phi_dBarD = phi_dD (since phi is a scalar) and phi_dBarDD (covariant derivative)
# \bar{D}_i \bar{D}_j \phi = \phi_{;\bar{i}\bar{j}} = \bar{D}_i \phi_{,j}
# = \phi_{,ij} - \bar{\Gamma}^k_{ij} \phi_{,k}
phi_dBarD = phi_dD
phi_dBarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
phi_dBarDD[i][j] = phi_dDD[i][j]
for k in range(DIM):
phi_dBarDD[i][j] += - GammabarUDD[k][i][j] * phi_dD[k]
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_stress_energy_source_terms.ipynb,
# this module will construct expressions for
# BSSN stress-energy source terms, in terms of
# elements of T^{mu nu}.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions
import sys # Standard Python modules for multiplatform OS-level functions
thismodule = __name__
# Define BSSN source terms in terms of T^{mu nu} or T_{mu nu}
def stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(inputvars,custom_T4UU=None):
# Step 1: Check if rfm.reference_metric() already called. If not, BSSN
# quantities are not yet defined, so cannot proceed!
if rfm.have_already_called_reference_metric_function == False:
print("BSSN_source_terms_ito_T4UU(): Must call reference_metric() first!")
sys.exit(1)
# Step 2.a: Define gamma4DD[mu][nu] = g_{mu nu} + n_{mu} n_{nu}
alpha = sp.symbols("alpha", real=True)
zero = sp.sympify(0)
n4D = [-alpha, zero, zero, zero]
AB4m.g4DD_ito_BSSN_or_ADM(inputvars)
gamma4DD = ixp.zerorank2(DIM=4)
for mu in range(4):
for nu in range(4):
gamma4DD[mu][nu] = AB4m.g4DD[mu][nu] + n4D[mu] * n4D[nu]
# Step 2.b: If expression for components of T4UU not given, declare T4UU here
if custom_T4UU == None:
T4UU = ixp.declarerank2("T4UU","sym01",DIM=4)
else:
T4UU = custom_T4UU
# Step 2.c: Define BSSN source terms
global SDD,SD,S,rho
# Step 2.c.i: S_{ij} = gamma_{i mu} gamma_{j nu} T^{mu nu}
SDD = ixp.zerorank2()
for i in range(3):
for j in range(3):
for mu in range(4):
for nu in range(4):
SDD[i][j] += gamma4DD[i+1][mu] * gamma4DD[j+1][nu] * T4UU[mu][nu]
# Step 2.c.ii: S_{i} = -gamma_{i mu} n_{nu} T^{mu nu}
SD = ixp.zerorank1()
for i in range(3):
for mu in range(4):
for nu in range(4):
SD[i] += - gamma4DD[i+1][mu] * n4D[nu] * T4UU[mu][nu]
# Step 2.c.iii: S = gamma^{ij} S_{ij}
if inputvars == "ADM":
gammaDD = ixp.declarerank2("gammaDD", "sym01")
gammaUU, dummydet = ixp.symm_matrix_inverter3x3(gammaDD) # Set gammaUU
elif inputvars == "BSSN":
import BSSN.ADM_in_terms_of_BSSN as AitoB # NRPy+: ADM quantities in terms of BSSN quantities
AitoB.ADM_in_terms_of_BSSN()
gammaUU = AitoB.gammaUU
S = zero
for i in range(3):
for j in range(3):
S += gammaUU[i][j] * SDD[i][j]
# Step 2.c.iv: rho = n_{mu} n_{nu} T^{mu nu}
rho = zero
for mu in range(4):
for nu in range(4):
rho += n4D[mu] * n4D[nu] * T4UU[mu][nu]
return SDD,SD,S,rho
# Step 3: Add BSSN stress-energy source terms to BSSN RHSs
def BSSN_source_terms_for_BSSN_RHSs(custom_T4UU=None):
global sourceterm_trK_rhs, sourceterm_a_rhsDD, sourceterm_lambda_rhsU, sourceterm_Lambdabar_rhsU
# Step 3.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho
if custom_T4UU == "unrescaled BSSN source terms already given":
SDD = ixp.declarerank2("SDD", "sym01")
SD = ixp.declarerank1("SD")
S = sp.symbols("S", real=True)
rho = sp.symbols("rho", real=True)
else:
SDD,SD,S,rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars("BSSN", custom_T4UU)
PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288")
alpha = sp.symbols("alpha", real=True)
# Step 3.b: trK_rhs
sourceterm_trK_rhs = 4 * PI * alpha * (rho + S)
# Step 3.c: Abar_rhsDD:
# Step 3.c.i: Compute trace-free part of S_{ij}:
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors() # Sets gammabarDD
gammabarUU, dummydet = ixp.symm_matrix_inverter3x3(Bq.gammabarDD) # Set gammabarUU
tracefree_SDD = ixp.zerorank2()
for i in range(3):
for j in range(3):
tracefree_SDD[i][j] = SDD[i][j]
for i in range(3):
for j in range(3):
for k in range(3):
for m in range(3):
tracefree_SDD[i][j] += -sp.Rational(1, 3) * Bq.gammabarDD[i][j] * gammabarUU[k][m] * SDD[k][m]
# Step 3.c.ii: Define exp_m4phi = e^{-4 phi}
Bq.phi_and_derivs()
# Step 3.c.iii: Evaluate stress-energy part of AbarDD's RHS
sourceterm_a_rhsDD = ixp.zerorank2()
for i in range(3):
for j in range(3):
Abar_rhsDDij = -8 * PI * alpha * Bq.exp_m4phi * tracefree_SDD[i][j]
sourceterm_a_rhsDD[i][j] = Abar_rhsDDij / rfm.ReDD[i][j]
# Step 3.d: Stress-energy part of Lambdabar_rhsU = stressenergy_Lambdabar_rhsU
sourceterm_Lambdabar_rhsU = ixp.zerorank1()
for i in range(3):
for j in range(3):
sourceterm_Lambdabar_rhsU[i] += -16 * PI * alpha * gammabarUU[i][j] * SD[j]
sourceterm_lambda_rhsU = ixp.zerorank1()
for i in range(3):
sourceterm_lambda_rhsU[i] = sourceterm_Lambdabar_rhsU[i] / rfm.ReU[i]
# Step 4: Add BSSN stress-energy source terms to BSSN constraints
def BSSN_source_terms_for_BSSN_constraints(custom_T4UU=None):
global sourceterm_H, sourceterm_MU
# Step 4.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho
if custom_T4UU == "unrescaled BSSN source terms already given":
SDD = ixp.declarerank2("SDD", "sym01")
SD = ixp.declarerank1("SD")
S = sp.symbols("S", real=True)
rho = sp.symbols("rho", real=True)
else:
SDD,SD,S,rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars("BSSN", custom_T4UU)
PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288")
# Step 4.b: Add source term to the Hamiltonian constraint H
sourceterm_H = -16 * PI * rho
# Step 4.c: Add source term to the momentum constraint M^i
# Step 4.c.i: Compute gammaUU in terms of BSSN quantities
import BSSN.ADM_in_terms_of_BSSN as AitoB
AitoB.ADM_in_terms_of_BSSN() # Provides gammaUU
# Step 4.c.ii: Raise S_i
SU = ixp.zerorank1()
for i in range(3):
for j in range(3):
SU[i] += AitoB.gammaUU[i][j] * SD[j]
# Step 4.c.iii: Add source term to momentum constraint & rescale:
sourceterm_MU = ixp.zerorank1()
for i in range(3):
sourceterm_MU[i] = -8 * PI * SU[i] / rfm.ReU[i]
# This module sets up Brill-Lindquist initial data in terms of
# the variables used in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# # Setting up Two Black Hole Initial Data, in Curvilinear Coordinates
#
# ## This module sets up initial data for two black holes at rest in spherical coordinates, converts the initial data/basis to the desired CoordSystem, and then rescales all quantities according to the BSSNCurvilinear prescription.
#
# ### Brill-Lindquist initial data ([Brill & Lindquist, Phys. Rev. 131, 471, 1963](https://journals.aps.org/pr/abstract/10.1103/PhysRev.131.471); see also Eq. 1 of [Brandt & Br\"ugmann, arXiv:gr-qc/9711015v1](https://arxiv.org/pdf/gr-qc/9711015v1.pdf)) may be written in terms of the BSSN conformal factor and ADM extrinsic curvature as
#
# $$\psi = e^{\phi} = 1 + \sum_{i=1}^N \frac{m_{(i)}}{2 \left|\vec{r}_{(i)} - \vec{r}\right|};\quad K_{ij}=0.$$
#
# These data consist of $N$ nonspinning black holes initially at rest. This module restricts to the case of two such black holes, positioned along either the $x$ or $z$ axis. Here, we implement $N=2$.
#
# **Inputs for $\psi$**:
# * The position and (bare) mass of black hole 1: $\left(x_{(1)},y_{(1)},z_{(1)}\right)$ and $m_{(1)}$, respectively
# * The position and (bare) mass of black hole 2: $\left(x_{(2)},y_{(2)},z_{(2)}\right)$ and $m_{(2)}$, respectively
#
# **Additional variables needed for spacetime evolution**:
# * Desired coordinate system
# * Desired initial lapse $\alpha$ and shift $\beta^i$
#
# **Transformation to curvilinear coordinates**:
# * Once the above variables have been set in Cartesian coordinates, we will apply the appropriate coordinate transformations and tensor rescalings ([described in the BSSN NRPy+ tutorial module](Tutorial-BSSNCurvilinear.ipynb))
# Step 1: Initialize core Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB
thismodule = __name__
BH1_posn_x, BH1_posn_y, BH1_posn_z = par.Cparameters("REAL", thismodule,
["BH1_posn_x", "BH1_posn_y", "BH1_posn_z"],
[0.0, 0.0, +0.5])
BH1_mass = par.Cparameters("REAL", thismodule, ["BH1_mass"], 1.0)
BH2_posn_x, BH2_posn_y, BH2_posn_z = par.Cparameters("REAL", thismodule,
["BH2_posn_x", "BH2_posn_y", "BH2_posn_z"],
[0.0, 0.0, -0.5])
BH2_mass = par.Cparameters("REAL", thismodule, ["BH2_mass"], 1.0)
# ComputeADMGlobalsOnly == True will only set up the ADM global quantities.
# == False will perform the full ADM SphorCart->BSSN Curvi conversion
def BrillLindquist(ComputeADMGlobalsOnly = False):
global Cartxyz,gammaCartDD, KCartDD, alphaCart, betaCartU, BCartU
# Step 2: Setting up Brill-Lindquist initial data
# Step 2.a: Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
global Cartxyz, gammaCartDD, KCartDD, alphaCart, betaCartU, BCartU
Cartxyz = ixp.declarerank1("Cartxyz")
# Step 2.b: Set psi, the conformal factor:
psi = sp.sympify(1)
psi += BH1_mass / ( 2 * sp.sqrt((Cartxyz[0]-BH1_posn_x)**2 + (Cartxyz[1]-BH1_posn_y)**2 + (Cartxyz[2]-BH1_posn_z)**2) )
psi += BH2_mass / ( 2 * sp.sqrt((Cartxyz[0]-BH2_posn_x)**2 + (Cartxyz[1]-BH2_posn_y)**2 + (Cartxyz[2]-BH2_posn_z)**2) )
# Step 2.c: Set all needed ADM variables in Cartesian coordinates
gammaCartDD = ixp.zerorank2()
KCartDD = ixp.zerorank2() # K_{ij} = 0 for these initial data
for i in range(DIM):
gammaCartDD[i][i] = psi**4
alphaCart = 1/psi**2
betaCartU = ixp.zerorank1() # We generally choose \beta^i = 0 for these initial data
BCartU = ixp.zerorank1() # We generally choose B^i = 0 for these initial data
if ComputeADMGlobalsOnly == True:
return
cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \
AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Cartesian",Cartxyz,
gammaCartDD,KCartDD,alphaCart,betaCartU,BCartU)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module implements the gammabar = gammahat constraint.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: import all needed modules from NRPy+:
from outputC import * # NRPy+: Core C code output module
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import os # Standard Python modules for multiplatform OS-level functions
def Enforce_Detgammabar_Constraint_symb_expressions():
# Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Then we set the coordinate system for the numerical grid
rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.
# We will need the h_{ij} quantities defined within BSSN_RHSs
# below when we enforce the gammahat=gammabar constraint
# Step 1: All barred quantities are defined in terms of BSSN rescaled gridfunctions,
# which we declare here in case they haven't yet been declared elsewhere.
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
hDD = Bq.hDD
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
# First define the Kronecker delta:
KroneckerDeltaDD = ixp.zerorank2()
for i in range(DIM):
KroneckerDeltaDD[i][i] = sp.sympify(1)
# The detgammabar in BSSN_RHSs is set to detgammahat when BSSN_RHSs::detgbarOverdetghat_equals_one=True (default),
# so we manually compute it here:
dummygammabarUU, detgammabar = ixp.symm_matrix_inverter3x3(gammabarDD)
# Next apply the constraint enforcement equation above.
hprimeDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
hprimeDD[i][j] = \
(nrpyAbs(rfm.detgammahat) / detgammabar) ** (sp.Rational(1, 3)) * (KroneckerDeltaDD[i][j] + hDD[i][j]) \
- KroneckerDeltaDD[i][j]
enforce_detg_constraint_symb_expressions = [
lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=hprimeDD[0][0]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=hprimeDD[0][1]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=hprimeDD[0][2]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=hprimeDD[1][1]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=hprimeDD[1][2]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=hprimeDD[2][2])]
return enforce_detg_constraint_symb_expressions
def output_Enforce_Detgammabar_Constraint_Ccode(outdir="BSSN/", exprs="", Read_xxs=False):
# Step 0: Check if outdir is string; error out if not.
check_if_string__error_if_not(outdir,"outdir")
desc = "Enforce det(gammabar) = det(gammahat) constraint."
name = "enforce_detgammabar_constraint"
params = "const rfm_struct *restrict rfmstruct,const paramstruct *restrict params, REAL *restrict in_gfs"
loopopts = "AllPoints,Enable_rfm_precompute"
if Read_xxs:
params = "const paramstruct *restrict params, REAL *restrict xx[3], REAL *restrict in_gfs"
loopopts = "AllPoints,Read_xxs"
outCfunction(
outfile=os.path.join(outdir, name + ".h"), desc=desc, name=name, params=params,
body=fin.FD_outputC("returnstring", exprs,
params="outCverbose=False,preindent=1,includebraces=False").replace("IDX4", "IDX4S"),
loopopts=loopopts)
// This function takes as input either (x,y,z) or (r,th,ph) and outputs
// all ADM quantities in the Cartesian or Spherical basis, respectively.
void ID_ADM_SphorCart(const REAL xyz_or_rthph[3],
REAL *gammaDD00,REAL *gammaDD01,REAL *gammaDD02,REAL *gammaDD11,REAL *gammaDD12,REAL *gammaDD22,
REAL *KDD00,REAL *KDD01,REAL *KDD02,REAL *KDD11,REAL *KDD12,REAL *KDD22,
REAL *alpha,
REAL *betaU0,REAL *betaU1,REAL *betaU2,
REAL *BU0,REAL *BU1,REAL *BU2) {
const REAL r = xyz_or_rthph[0];
const REAL th = xyz_or_rthph[1];
const REAL ph = xyz_or_rthph[2];
const double tmp0 = (1.0/4.0)*M;
const double tmp1 = pow(M, 2);
const double tmp2 = pow(chi, 2)*tmp1;
const double tmp3 = -tmp2;
const double tmp4 = sqrt(tmp1 + tmp3);
const double tmp5 = (1.0/4.0)*tmp4;
const double tmp6 = cos(th);
const double tmp7 = 1.0/r;
const double tmp8 = (1.0/4.0)*tmp7*(M + tmp4) + 1;
const double tmp9 = pow(r, 2)*pow(tmp8, 4);
const double tmp10 = tmp2*pow(tmp6, 2) + tmp9;
const double tmp11 = r*pow(tmp8, 2);
const double tmp12 = -M + tmp11 + tmp4;
const double tmp13 = tmp2 + tmp9;
const double tmp14 = 2*tmp11;
const double tmp15 = sin(th);
const double tmp16 = pow(tmp15, 2);
const double tmp17 = tmp16*tmp2;
const double tmp18 = pow(tmp13, 2) - tmp17*(-M*tmp14 + tmp13);
const double tmp19 = 1.0/tmp10;
const double tmp20 = tmp16*tmp19;
const double tmp21 = pow(tmp10*tmp18, -1.0/2.0);
const double tmp22 = pow(M, 4);
*gammaDD00 = tmp10*pow(r + tmp0 + tmp5, 2)/(pow(r, 3)*tmp12);
*gammaDD01 = 0;
*gammaDD02 = 0;
*gammaDD11 = tmp10;
*gammaDD12 = 0;
*gammaDD22 = tmp18*tmp20;
*KDD00 = 0;
*KDD01 = 0;
*KDD02 = chi*tmp1*tmp20*tmp21*tmp8*(-pow(chi, 4)*tmp22 + 3*pow(r, 4)*pow(tmp8, 8) - tmp17*(tmp3 + tmp9) + 2*tmp2*tmp9)/sqrt(r*tmp12);
*KDD11 = 0;
*KDD12 = -pow(chi, 3)*tmp14*pow(tmp15, 3)*tmp19*tmp21*tmp22*tmp6*sqrt(tmp12*tmp7)*(r - tmp0 - tmp5);
*KDD22 = 0;
*alpha = 1;
*betaU0 = 0;
*betaU1 = 0;
*betaU2 = 0;
*BU0 = 0;
*BU1 = 0;
*BU2 = 0;
}
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4.ipynb,
# this module will construct a generic
# expression for \psi_4
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
def Psi4(specify_tetrad=True):
global psi4_im_pt, psi4_re_pt
# Step 1.b: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 1.c: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.d: Import all ADM quantities as written in terms of BSSN quantities
import BSSN.ADM_in_terms_of_BSSN as AB
AB.ADM_in_terms_of_BSSN()
# Step 1.e: Set up tetrad vectors
if specify_tetrad==True:
import BSSN.Psi4_tetrads as BP4t
BP4t.Psi4_tetrads()
mre4U = BP4t.mre4U
mim4U = BP4t.mim4U
n4U = BP4t.n4U
else:
# For code validation against NRPy+ psi_4 tutorial module (Tutorial-Psi4.ipynb);
# ensures a more complete code validation.
mre4U = ixp.declarerank1("mre4U", DIM=4)
mim4U = ixp.declarerank1("mim4U", DIM=4)
n4U = ixp.declarerank1("n4U", DIM=4)
# Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric:
RDDDD = ixp.zerorank4()
gammaDDdDD = AB.gammaDDdDD
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
RDDDD[i][k][l][m] = sp.Rational(1, 2) * \
(gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] -
gammaDDdDD[k][m][i][l])
# ... then we add the term on the right:
gammaDD = AB.gammaDD
GammaUDD = AB.GammaUDD
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
for n in range(DIM):
for p in range(DIM):
RDDDD[i][k][l][m] += gammaDD[n][p] * \
(GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l])
# Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
rank4term1DDDD = ixp.zerorank4()
KDD = AB.KDD
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank4term1DDDD[i][j][k][l] = RDDDD[i][j][k][l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j]
# Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
rank3term2DDD = ixp.zerorank3()
KDDdD = AB.KDDdD
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank3term2DDD[j][k][l] = sp.Rational(1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k])
# ... then we construct the second term in this sum:
# \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
for p in range(DIM):
rank3term2DDD[j][k][l] += sp.Rational(1, 2) * (
GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p])
# Finally, we multiply the term by $-8$:
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank3term2DDD[j][k][l] *= sp.sympify(-8)
# Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
# Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml}
RDD = ixp.zerorank2()
gammaUU = AB.gammaUU
for j in range(DIM):
for l in range(DIM):
for i in range(DIM):
for m in range(DIM):
RDD[j][l] += gammaUU[i][m]*RDDDD[i][j][m][l]
# Step 5.2: Construct K^p_l = gamma^{pi} K_{il}
KUD = ixp.zerorank2()
for p in range(DIM):
for l in range(DIM):
for i in range(DIM):
KUD[p][l] += gammaUU[p][i]*KDD[i][l]
# Step 5.3: Construct trK = gamma^{ij} K_{ij}
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j]*KDD[i][j]
# Next we put these terms together to construct the entire term in parentheses:
# +4 \left(R_{jl} - K_{jp} K^p_l + K K_{jl} \right),
rank2term3DD = ixp.zerorank2()
for j in range(DIM):
for l in range(DIM):
rank2term3DD[j][l] = RDD[j][l] + trK*KDD[j][l]
for p in range(DIM):
rank2term3DD[j][l] += - KDD[j][p]*KUD[p][l]
# Finally we multiply by +4:
for j in range(DIM):
for l in range(DIM):
rank2term3DD[j][l] *= sp.sympify(4)
# Step 6: Construct real & imaginary parts of psi_4
# by contracting constituent rank 2, 3, and 4
# tensors with input tetrads mre4U, mim4U, & n4U.
def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta):
return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[eta] * Mim[delta]
def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta):
return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[eta] * Mre[delta]
# We split psi_4 into three pieces, to expedite & possibly parallelize C code generation.
psi4_re_pt = [sp.sympify(0),sp.sympify(0),sp.sympify(0)]
psi4_im_pt = [sp.sympify(0),sp.sympify(0),sp.sympify(0)]
# First term:
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
psi4_re_pt[0] += rank4term1DDDD[i][j][k][l] * tetrad_product__Real_psi4(n4U, mre4U, mim4U,
i + 1, j + 1,k + 1, l + 1)
psi4_im_pt[0] += rank4term1DDDD[i][j][k][l] * tetrad_product__Imag_psi4(n4U, mre4U, mim4U,
i + 1, j + 1, k + 1, l + 1)
# Second term:
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
psi4_re_pt[1] += rank3term2DDD[j][k][l] * \
sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1))
psi4_im_pt[1] += rank3term2DDD[j][k][l] * \
sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1))
# Third term:
for j in range(DIM):
for l in range(DIM):
psi4_re_pt[2] += rank2term3DD[j][l] * \
(sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0)
+ tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
psi4_im_pt[2] += rank2term3DD[j][l] * \
(sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0)
+ tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4_tetrads.ipynb,
# this module will construct tetrads
# needed to compute \psi_4 (as well as other
# Weyl scalars and invariants in principle)
# Authors: Zachariah B. Etienne
# (zachetie **at** gmail **dot* com),
# and Patrick Nelson
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
import reference_metric as rfm
# Step 1.b: Initialize TetradChoice parameter
thismodule = __name__
# Current option: QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char", thismodule, "TetradChoice", "QuasiKinnersley"))
par.initialize_param(par.glb_param("char", thismodule, "UseCorrectUnitNormal", "False"))
def Psi4_tetrads():
global l4U, n4U, mre4U, mim4U
# Step 1.c: Check if tetrad choice is implemented:
if par.parval_from_str(thismodule+"::TetradChoice") != "QuasiKinnersley":
print("ERROR: "+thismodule+"::TetradChoice = "+par.parval_from_str("TetradChoice")+" currently unsupported!")
exit(1)
# Step 1.d: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.f: Import all ADM quantities as written in terms of BSSN quantities
# import BSSN.ADM_in_terms_of_BSSN as AB
# AB.ADM_in_terms_of_BSSN()
# Step 2.a: Declare the Cartesian x,y,z as input parameters
# and v_1^a, v_2^a, and v_3^a tetrads,
# as well as detgamma and gammaUU from
# BSSN.ADM_in_terms_of_BSSN
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
# x, y, z = par.Cparameters("REAL", thismodule, ["x", "y", "z"])
v1UCart = ixp.zerorank1()
v2UCart = ixp.zerorank1()
gammaDD = ixp.declarerank2("gammaDD","sym01")
gammaUU,detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
# detgamma = AB.detgamma
# gammaUU = AB.gammaUU
# Step 2.b: Define v1U and v2U
v1UCart = [-y, x, sp.sympify(0)]
v2UCart = [x, y, z]
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
for i in range(DIM):
v1U[i] = v1UCart[i]
v2U[i] = v2UCart[i]
# # Step 2.c: Construct the Jacobian d x_Cart^i / d xx^j
# Jac_dUCart_dDrfmUD = ixp.zerorank2()
# for i in range(DIM):
# for j in range(DIM):
# Jac_dUCart_dDrfmUD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j])
#
# # Step 2.d: Invert above Jacobian to get needed d xx^j / d x_Cart^i
# Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUCart_dDrfmUD)
#
# # Step 2.e: Transform v1U and v2U from the Cartesian to the xx^i basis
# v1U = ixp.zerorank1()
# v2U = ixp.zerorank1()
# for i in range(DIM):
# for j in range(DIM):
# v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j]
# v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j]
# Step 2.f: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 2.g: Define v3U
v3U = ixp.zerorank1()
LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3)
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]
# Step 2.h: Define omega_{ij}
omegaDD = ixp.zerorank2()
#gammaDD = AB.gammaDD
def v_vectorDU(v1U,v2U,v3U, i,a):
if i==0:
return v1U[a]
elif i==1:
return v2U[a]
elif i==2:
return v3U[a]
else:
print("ERROR: unknown vector!")
exit(1)
def update_omega(omegaDD, i,j, v1U,v2U,v3U,gammaDD):
omegaDD[i][j] = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omegaDD[i][j] += v_vectorDU(v1U,v2U,v3U, i,a)*v_vectorDU(v1U,v2U,v3U, j,b)*gammaDD[a][b]
# Step 2.i: Define e^a_i. Note that:
# omegaDD[0][0] = \omega_{11} above;
# omegaDD[1][1] = \omega_{22} above, etc.
# First e_1^a: Orthogonalize & normalize:
e1U = ixp.zerorank1()
update_omega(omegaDD, 0,0, v1U,v2U,v3U,gammaDD)
for a in range(DIM):
e1U[a] = v1U[a]/sp.sqrt(omegaDD[0][0])
# Next e_2^a: First orthogonalize:
e2U = ixp.zerorank1()
update_omega(omegaDD, 0,1, e1U,v2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] = (v2U[a] - omegaDD[0][1]*e1U[a])
# Then normalize:
update_omega(omegaDD, 1,1, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] /= sp.sqrt(omegaDD[1][1])
# Next e_3^a: First orthogonalize:
e3U = ixp.zerorank1()
update_omega(omegaDD, 0,2, e1U,e2U,v3U,gammaDD)
update_omega(omegaDD, 1,2, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e3U[a] = (v3U[a] - omegaDD[0][2]*e1U[a] - omegaDD[1][2]*e2U[a])
# Then normalize:
update_omega(omegaDD, 2,2, e1U,e2U,e3U,gammaDD)
for a in range(DIM):
e3U[a] /= sp.sqrt(omegaDD[2][2])
# Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
r4U = ixp.zerorank1(DIM=4)
u4U = ixp.zerorank1(DIM=4)
theta4U = ixp.zerorank1(DIM=4)
phi4U = ixp.zerorank1(DIM=4)
for a in range(DIM):
r4U[ a+1] = e2U[a]
theta4U[a+1] = e3U[a]
phi4U[ a+1] = e1U[a]
# FIXME? assumes alpha=1, beta^i = 0
if par.parval_from_str(thismodule+"::UseCorrectUnitNormal") == "False":
u4U[0] = 1
else:
# Eq. 2.116 in Baumgarte & Shapiro:
# n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1.
import BSSN.BSSN_quantities as Bq
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
Bq.BSSN_basic_tensors()
u4U[0] = 1/Bq.alpha
for i in range(DIM):
u4U[i+1] = -Bq.betaU[i]/Bq.alpha
l4U = ixp.zerorank1(DIM=4)
n4U = ixp.zerorank1(DIM=4)
mre4U = ixp.zerorank1(DIM=4)
mim4U = ixp.zerorank1(DIM=4)
isqrt2 = 1/sp.sqrt(2)
for mu in range(4):
l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])
n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])
mre4U[mu] = isqrt2*theta4U[mu]
mim4U[mu] = isqrt2* phi4U[mu]
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
"<script>\n",
" window.dataLayer = window.dataLayer || [];\n",
" function gtag(){dataLayer.push(arguments);}\n",
" gtag('js', new Date());\n",
"\n",
" gtag('config', 'UA-59152712-8');\n",
"</script>\n",
"\n",
"# Start-to-Finish validation of $\\psi_4$ in curvilinear coordinates against Cartesian formulation provided by [Patrick Nelson's Weyl scalars & invariants in Cartesian coordinates module](../../Tutorial-WeylScalarsInvariants-Cartesian.ipynb)\n",
"\n",
"### Author: Zach Etienne\n",
"\n",
"<font color='blue'>**This module exists as a modification of [the NRPy+ $\\psi_4$ in curvilinear coordinates module](../../Tutorial-Psi4.ipynb), writing all spacetime quantities in terms of ADM variables and their derivatives directly.**</font>\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 module).\n",
"\n",
"<a id='toc'></a>\n",
"\n",
"# Introduction, Table of Contents\n",
"$$\\label{toc}$$\n",
"\n",
"This module constructs $\\psi_4$, 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 imaginary, 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. The [next module on tetrads defines these tetrad quantities](Tutorial-Psi4_tetrads.ipynb) (currently only a quasi-Kinnersley tetrad is supported).\n",
"\n",
"**This tutorial module is organized as follows:**\n",
"\n",
"1. [Step 1](#initializenrpy): Initialize needed NRPy+ modules\n",
"1. [Step 2](#riemann): Constructing the 3-Riemann tensor $R_{ik\\ell m}$\n",
"1. [Step 3](#termone): Constructing the rank-4 tensor in Term 1 of $\\psi_4$: $R_{ijkl} + 2 K_{i[k} K_{l]j}$\n",
"1. [Step 4](#termtwo): Constructing the rank-3 tensor in Term 2 of $\\psi_4$: $-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)$\n",
"1. [Step 5](#termthree): Constructing the rank-2 tensor in term 3 of $\\psi_4$: $+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right)$\n",
"1. [Step 6](#psifour): Constructing $\\psi_4$ through contractions of the above terms with arbitrary tetrad vectors $n^\\mu$ and $m^\\mu$\n",
"1. [Step 7](#code_validation): Code Validation against BSSN.Psi4 NRPy+ module\n",
"1. [Step 8](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='initializenrpy'></a>\n",
"\n",
"# Step 1: Initialize core NRPy+ modules \\[Back to [top](#toc)\\]\n",
"$$\\label{initializenrpy}$$\n",
"\n",
"Let's start by importing all the needed modules from NRPy+:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Step 1.a: import all needed modules from NRPy+:\n",
"import sympy as sp\n",
"from outputC import *\n",
"import NRPy_param_funcs as par\n",
"import indexedexp as ixp\n",
"import grid as gri\n",
"import finite_difference as fin\n",
"import reference_metric as rfm\n",
"\n",
"# Step 1.b: Set the coordinate system for the numerical grid\n",
"par.set_parval_from_str(\"reference_metric::CoordSystem\",\"Cartesian\")\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()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='riemann'></a>\n",
"\n",
"# Step 2: Constructing the 3-Riemann tensor $R_{ik\\ell m}$ \\[Back to [top](#toc)\\]\n",
"$$\\label{riemann}$$\n",
"\n",
"Analogously to Christoffel symbols, the Riemann tensor is a measure of the curvature of an $N$-dimensional manifold. Thus the 3-Riemann tensor is not simply a projection of the 4-Riemann tensor (see e.g., Eq. 2.7 of [Campanelli *et al* (1998)](https://arxiv.org/pdf/gr-qc/9803058.pdf) for the relation between 4-Riemann and 3-Riemann), as $N$-dimensional Riemann tensors are meant to define a notion of curvature given only the associated $N$-dimensional metric. \n",
"\n",
"So, given the ADM 3-metric, the Riemann tensor in arbitrary dimension is given by the 3-dimensional version of Eq. 1.19 in Baumgarte & Shapiro's *Numerical Relativity*. I.e.,\n",
"\n",
"$$\n",
"R^i_{jkl} = \\partial_k \\Gamma^{i}_{jl} - \\partial_l \\Gamma^{i}_{jk} + \\Gamma^i_{mk} \\Gamma^m_{jl} - \\Gamma^{i}_{ml} \\Gamma^{m}_{jk},\n",
"$$\n",
"where $\\Gamma^i_{jk}$ is the Christoffel symbol associated with the 3-metric $\\gamma_{ij}$:\n",
"\n",
"$$\n",
"\\Gamma^l_{ij} = \\frac{1}{2} \\gamma^{lk} \\left(\\gamma_{ki,j} + \\gamma_{kj,i} - \\gamma_{ij,k} \\right) \n",
"$$\n",
"\n",
"Notice that this equation for the Riemann tensor is equivalent to the equation given in the Wikipedia article on [Formulas in Riemannian geometry](https://en.wikipedia.org/w/index.php?title=List_of_formulas_in_Riemannian_geometry&oldid=882667524):\n",
"\n",
"$$\n",
"R^\\ell{}_{ijk}=\n",
"\\partial_j \\Gamma^\\ell{}_{ik}-\\partial_k\\Gamma^\\ell{}_{ij}\n",
"+\\Gamma^\\ell{}_{js}\\Gamma_{ik}^s-\\Gamma^\\ell{}_{ks}\\Gamma^s{}_{ij},\n",
"$$\n",
"with the replacements $i\\to \\ell$, $j\\to i$, $k\\to j$, $l\\to k$, and $s\\to m$. Wikipedia also provides a simpler form in terms of second-derivatives of three-metric itself (using the definition of Christoffel symbol), so that we need not define derivatives of the Christoffel symbol:\n",
"\n",
"$$\n",
"R_{ik\\ell m}=\\frac{1}{2}\\left(\n",
"\\gamma_{im,k\\ell} \n",
"+ \\gamma_{k\\ell,im}\n",
"- \\gamma_{i\\ell,km}\n",
"- \\gamma_{km,i\\ell} \\right)\n",
"+\\gamma_{np} \\left(\n",
"\\Gamma^n{}_{k\\ell} \\Gamma^p{}_{im} - \n",
"\\Gamma^n{}_{km} \\Gamma^p{}_{i\\ell} \\right).\n",
"$$\n",
"\n",
"First we construct the term on the left:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric:\n",
"RDDDD = ixp.zerorank4()\n",
"gammaDD = ixp.register_gridfunctions_for_single_rank2(\"AUX\",\"gammaDD\", \"sym01\") # The AUX or EVOL designation is *not*\n",
" # used in diagnostic modules.\n",
"kDD = ixp.register_gridfunctions_for_single_rank2(\"AUX\",\"kDD\", \"sym01\")\n",
"gammaDD_dD = ixp.declarerank3(\"gammaDD_dD\",\"sym01\")\n",
"gammaDD_dDD = ixp.declarerank4(\"gammaDD_dDD\",\"sym01_sym23\")\n",
"\n",
"# gammaDD_dDD = AB.gammaDD_dDD\n",
"\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" RDDDD[i][k][l][m] = sp.Rational(1,2) * \\\n",
" (gammaDD_dDD[i][m][k][l] + gammaDD_dDD[k][l][i][m] - gammaDD_dDD[i][l][k][m] - gammaDD_dDD[k][m][i][l])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... then we add the term on the right:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# ... then we add the term on the right:\n",
"# Define the Christoffel symbols\n",
"GammaUDD = ixp.zerorank3(DIM)\n",
"gammaUU,gammadetdummy = ixp.symm_matrix_inverter3x3(gammaDD)\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\\\n",
" (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m])\n",
"\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" for n in range(DIM):\n",
" for p in range(DIM):\n",
" RDDDD[i][k][l][m] += gammaDD[n][p] * \\\n",
" (GammaUDD[n][k][l]*GammaUDD[p][i][m] - GammaUDD[n][k][m]*GammaUDD[p][i][l])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termone'></a>\n",
"\n",
"# Step 3: Constructing the rank-4 tensor in Term 1 of $\\psi_4$: $R_{ijkl} + 2 K_{i[k} K_{l]j}$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termone}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-4 tensor in the first term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"R_{ijkl} + 2 K_{i[k} K_{l]j} = R_{ijkl} + K_{ik} K_{lj} - K_{il} K_{kj}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"rank4term1 = ixp.zerorank4()\n",
"# kDD = AB.kDD\n",
"\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank4term1[i][j][k][l] = RDDDD[i][j][k][l] + kDD[i][k]*kDD[l][j] - kDD[i][l]*kDD[k][j]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termtwo'></a>\n",
"\n",
"# Step 4: Constructing the rank-3 tensor in Term 2 of $\\psi_4$: $-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termtwo}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-3 tensor in the second term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)\n",
"$$\n",
"First let's construct the first term in this sum: $K_{j[k,l]} = \\frac{1}{2} (K_{jk,l} - K_{jl,k})$:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"rank3term2 = ixp.zerorank3()\n",
"# kDD_dD = AB.kDD_dD\n",
"kDD_dD = ixp.declarerank3(\"kDD_dD\",\"sym01\")\n",
"\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank3term2[j][k][l] = sp.Rational(1,2)*(kDD_dD[j][k][l] - kDD_dD[j][l][k])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... then we construct the second term in this sum: $\\Gamma^{p}_{j[k} K_{l]p} = \\frac{1}{2} (\\Gamma^{p}_{jk} K_{lp}-\\Gamma^{p}_{jl} K_{kp})$:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# ... then we construct the second term in this sum: \n",
"# \\Gamma^{p}_{j[k} K_{l]p} = \\frac{1}{2} (\\Gamma^{p}_{jk} K_{lp}-\\Gamma^{p}_{jl} K_{kp}):\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for p in range(DIM):\n",
" rank3term2[j][k][l] += sp.Rational(1,2)*(GammaUDD[p][j][k]*kDD[l][p] - GammaUDD[p][j][l]*kDD[k][p])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we multiply the term by $-8$:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# Finally, we multiply the term by $-8$:\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank3term2[j][k][l] *= sp.sympify(-8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termthree'></a>\n",
"\n",
"# Step 5: Constructing the rank-2 tensor in term 3 of $\\psi_4$: $+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right)$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termthree}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-2 tensor in the third term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"$$\n",
"where\n",
"\\begin{align}\n",
"R_{jl} &= R^i_{jil} \\\\\n",
"&= \\gamma^{im} R_{ijml} \\\\\n",
"K &= K^i_i \\\\\n",
"&= \\gamma^{im} K_{im}\n",
"\\end{align}\n",
"\n",
"Let's build the components of this term: $R_{jl}$, $K^p_l$, and $K$, as defined above:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"\n",
"# Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml}\n",
"RDD = ixp.zerorank2()\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" for i in range(DIM):\n",
" for m in range(DIM):\n",
" RDD[j][l] += gammaUU[i][m]*RDDDD[i][j][m][l]\n",
"\n",
"# Step 5.2: Construct K^p_l = gamma^{pi} K_{il}\n",
"KUD = ixp.zerorank2()\n",
"for p in range(DIM):\n",
" for l in range(DIM):\n",
" for i in range(DIM):\n",
" KUD[p][l] += gammaUU[p][i]*kDD[i][l]\n",
"\n",
"# Step 5.3: Construct trK = gamma^{ij} K_{ij}\n",
"trK = sp.sympify(0)\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" trK += gammaUU[i][j]*kDD[i][j]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we put these terms together to construct the entire term:\n",
"$$\n",
"+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"# Next we put these terms together to construct the entire term in parentheses:\n",
"# +4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"rank2term3 = ixp.zerorank2()\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" rank2term3[j][l] = RDD[j][l] + trK*kDD[j][l]\n",
" for p in range(DIM):\n",
" rank2term3[j][l] += - kDD[j][p]*KUD[p][l]\n",
"# Finally we multiply by +4:\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" rank2term3[j][l] *= sp.sympify(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='psifour'></a>\n",
"\n",
"# Step 6: Constructing $\\psi_4$ through contractions of the above terms with an arbitrary tetrad vectors $m^\\mu$ and $n^\\mu$ \\[Back to [top](#toc)\\]\n",
"$$\\label{psifour}$$\n",
"\n",
"Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf) writes $\\psi_4$ (which is complex) as the contraction of each of the above terms with products of tetrad vectors:\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",
"where $\\bar{m}^\\mu$ is the complex conjugate of $m^\\mu$, and $n^\\mu$ is real. The third term is given by\n",
"\\begin{align}\n",
"{n}^{[0}\\bar{m}^{j]}{n}^{[0}\\bar{m}^{l]}\n",
"&= \\frac{1}{2}({n}^{0}\\bar{m}^{j} - {n}^{j}\\bar{m}^{0} )\\frac{1}{2}({n}^{0}\\bar{m}^{l} - {n}^{l}\\bar{m}^{0} )\\\\\n",
"&= \\frac{1}{4}({n}^{0}\\bar{m}^{j} - {n}^{j}\\bar{m}^{0} )({n}^{0}\\bar{m}^{l} - {n}^{l}\\bar{m}^{0} )\\\\\n",
"&= \\frac{1}{4}({n}^{0}\\bar{m}^{j}{n}^{0}\\bar{m}^{l} - {n}^{j}\\bar{m}^{0}{n}^{0}\\bar{m}^{l} - {n}^{0}\\bar{m}^{j}{n}^{l}\\bar{m}^{0} + {n}^{j}\\bar{m}^{0}{n}^{l}\\bar{m}^{0})\n",
"\\end{align}\n",
"\n",
"Only $m^\\mu$ is complex, so we can separate the real and imaginary parts of $\\psi_4$ by hand, defining $M^\\mu$ to now be the real part of $\\bar{m}^\\mu$ and $\\mathcal{M}^\\mu$ to be the imaginary part. All of the above products are of the form ${n}^\\mu\\bar{m}^\\nu{n}^\\eta\\bar{m}^\\delta$, so let's evalute the real and imaginary parts of this product once, for all such terms:\n",
"\n",
"\\begin{align}\n",
"{n}^\\mu\\bar{m}^\\nu{n}^\\eta\\bar{m}^\\delta\n",
"&= {n}^\\mu(M^\\nu - i \\mathcal{M}^\\nu){n}^\\eta(M^\\delta - i \\mathcal{M}^\\delta) \\\\\n",
"&= \\left({n}^\\mu M^\\nu {n}^\\eta M^\\delta -\n",
"{n}^\\mu \\mathcal{M}^\\nu {n}^\\eta \\mathcal{M}^\\delta \\right)+\n",
"i \\left(\n",
"-{n}^\\mu M^\\nu {n}^\\eta \\mathcal{M}^\\delta\n",
"-{n}^\\mu \\mathcal{M}^\\nu {n}^\\eta M^\\delta\n",
"\\right)\n",
"\\end{align}\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::M_PI\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::xmin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::xmax\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::ymin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::ymax\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::zmin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::zmax\n"
]
}
],
"source": [
"# mre4U = ixp.declarerank1(\"mre4U\",DIM=4)\n",
"# mim4U = ixp.declarerank1(\"mim4U\",DIM=4)\n",
"# n4U = ixp.declarerank1(\"n4U\" ,DIM=4)\n",
"\n",
"import BSSN.Psi4_tetrads as P4t\n",
"P4t.Psi4_tetrads()\n",
"mre4U = P4t.mre4U\n",
"mim4U = P4t.mim4U\n",
"n4U = P4t.n4U\n",
"\n",
"def tetrad_product__Real_psi4(n,Mre,Mim, mu,nu,eta,delta):\n",
" return +n[mu]*Mre[nu]*n[eta]*Mre[delta] - n[mu]*Mim[nu]*n[eta]*Mim[delta]\n",
"\n",
"def tetrad_product__Imag_psi4(n,Mre,Mim, mu,nu,eta,delta):\n",
" return -n[mu]*Mre[nu]*n[eta]*Mim[delta] - n[mu]*Mim[nu]*n[eta]*Mre[delta]\n",
"\n",
"\n",
"psi4_re = sp.sympify(0)\n",
"psi4_im = sp.sympify(0)\n",
"# First term:\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank4term1[i][j][k][l]*tetrad_product__Real_psi4(n4U,mre4U,mim4U, i+1,j+1,k+1,l+1)\n",
" psi4_im += rank4term1[i][j][k][l]*tetrad_product__Imag_psi4(n4U,mre4U,mim4U, i+1,j+1,k+1,l+1)\n",
"\n",
"# Second term:\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank3term2[j][k][l] * \\\n",
" sp.Rational(1,2)*(+tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,k+1,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,k+1,l+1) )\n",
" psi4_im += rank3term2[j][k][l] * \\\n",
" sp.Rational(1,2)*(+tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,k+1,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,k+1,l+1) )\n",
"# Third term:\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank2term3[j][l] * \\\n",
" (sp.Rational(1,4)*(+tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,0,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,0,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,l+1,0)\n",
" +tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,l+1,0)))\n",
" psi4_im += rank2term3[j][l] * \\\n",
" (sp.Rational(1,4)*(+tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,0,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,0,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,l+1,0)\n",
" +tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,l+1,0)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='code_validation'></a>\n",
"\n",
"# Step 6: Code validation against BSSN.Psi4 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 module.\n",
"\n",
"By default, we compare all quantities in Spherical coordinates, though other coordinate systems may be chosen."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"STARTING NEW\n",
"Wrote to file \"Psi4_new.h\"\n",
"FINISHED NEW\n",
"STARTING OLD\n",
"Wrote to file \"Psi4_old.h\"\n",
"FINISHED OLD\n"
]
}
],
"source": [
"outCparams = \"preindent=1,outCfileaccess=w,outCverbose=False,includebraces=False\"\n",
"print(\"STARTING NEW\")\n",
"fin.FD_outputC(\"Psi4_new.h\", lhrh(lhs=\"psi4_real\", rhs=psi4_im), outCparams)\n",
"print(\"FINISHED NEW\")\n",
"\n",
"gri.glb_gridfcs_list = []\n",
"\n",
"import WeylScal4NRPy.WeylScalars_Cartesian as W4\n",
"W4.WeylScalars_Cartesian()\n",
"print(\"STARTING OLD\")\n",
"fin.FD_outputC(\"Psi4_old.h\", lhrh(lhs=\"psi4_real\", rhs=W4.psi4i), outCparams)\n",
"print(\"FINISHED OLD\")\n",
"\n",
"# print(\"FullSimplify[\"+str(sp.mathematica_code(psi4_re-W4.psi4r))+\"]\")\n",
"# with open(\"math.txt\",\"w\") as file:\n",
"# file.write(\"FullSimplify[\"+str(sp.mathematica_code(psi4_re-W4.psi4r))+\"]\")\n",
" \n",
"# # Call the BSSN_RHSs() function from within the\n",
"# # BSSN/BSSN_RHSs.py module,\n",
"# # which should do exactly the same as in Steps 1-16 above.\n",
"# print(\"vvv Ignore the minor warnings below. vvv\")\n",
"# import BSSN.Psi4 as BP4\n",
"# BP4.Psi4()\n",
"# print(\"^^^ Ignore the minor warnings above. ^^^\\n\")\n",
"\n",
"# print(\"Consistency check between this tutorial and BSSN.Psi4 NRPy+ module: ALL SHOULD BE ZERO.\")\n",
"\n",
"# print(\"psi4_im - BP4.psi4_im = \" + str(psi4_im - BP4.psi4_im))\n",
"# print(\"psi4_re - BP4.psi4_re = \" + str(psi4_re - BP4.psi4_re))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GOOD: 9.858968807130199e-16 : 1.678026634904849e-08 1.678026634904847e-08\r\n"
]
}
],
"source": [
"!gcc -O2 psi4_tester.c -o psi4_tester -lm\n",
"!./psi4_tester 4 4 4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='latex_pdf_output'></a>\n",
"\n",
"# Step 7: Output this notebook to $\\LaTeX$-formatted PDF file \\[Back to [top](#toc)\\]\n",
"$$\\label{latex_pdf_output}$$"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[NbConvertApp] Converting notebook Tutorial-Psi4.ipynb to latex\n",
"[NbConvertApp] Writing 61056 bytes to Tutorial-Psi4.tex\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n"
]
}
],
"source": [
"!jupyter nbconvert --to latex --template latex_nrpy_style.tplx --log-level='WARN' Tutorial-Psi4.ipynb\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!rm -f Tut*.out Tut*.aux Tut*.log"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
# This code calculates the Weyl scalars psi0, psi1, psi2, psi3, and psi4. It does so by following the paper
# Baker, Campanelli, and Lousto. PRD 65, 044001 (2002), gr-qc/0104063 and the example set by the Kranc-
# generated ETK thorn which can be found at https://bitbucket.org/einsteintoolkit/einsteinanalysis/src
# Step 1: import all needed modules from NRPy+:
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import sympy as sp
# Step 2: Initialize WeylScalars parameters
thismodule = __name__
# Current option: Approx_QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char *", thismodule, "TetradChoice", "Approx_QuasiKinnersley"))
# This controls what gets output. Acceptable values are "psi4_only", "all_psis", and "all_psis_and_invariants"
par.initialize_param(par.glb_param("char *", thismodule, "output_scalars", "all_psis_and_invariants"))
# Step 3: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other possible uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 1: Call BSSNs. This module computes many different quantities related to the metric,
# many of which will be essential here. We must first change to our desired coordinate
# system, however.
def WeylScalars_Cartesian():
# We do not need the barred or hatted quantities calculated when using Cartesian coordinates.
# Instead, we declare the PHYSICAL metric and extrinsic curvature as grid functions.
gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01")
kDD = ixp.register_gridfunctions_for_single_rank2("AUX","kDD", "sym01")
gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
output_scalars = par.parval_from_str("output_scalars")
global psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i
# if output_scalars is "all_psis_and_invariants":
# psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = sp.symbols("psi4r psi4i\
# psi3r psi3i\
# psi2r psi2i\
# psi1r psi1i\
# psi0r psi0i")
# elif output_scalars is "all_psis":
psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = gri.register_gridfunctions("AUX",["psi4r","psi4i",\
"psi3r","psi3i",\
"psi2r","psi2i",\
"psi1r","psi1i",\
"psi0r","psi0i"])
# Step 2a: Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
# Step 2b: Set the coordinate system to Cartesian
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
# Step 2c: Set which tetrad is used; at the moment, only one supported option
if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice") == "Approx_QuasiKinnersley":
# Step 3a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal
# direction, one in the radial direction, and the cross product of the two.
# Eqs 5.6, 5.7 in https://arxiv.org/pdf/gr-qc/0104063.pdf:
# v_1^a &= [-y,x,0] \\
# v_2^a &= [x,y,z] \\
# v_3^a &= {\rm det}(g)^{1/2} g^{ad} \epsilon_{dbc} v_1^b v_2^c,
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
v3U = ixp.zerorank1()
v1U[0] = -y
v1U[1] = x
v1U[2] = sp.sympify(0)
v2U[0] = x
v2U[1] = y
v2U[2] = z
LeviCivitaSymbol_rank3 = define_LeviCivitaSymbol_rank3()
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbol_rank3[d][b][c] * v1U[b] *v2U[c]
# Step 3b: Gram-Schmidt orthonormalization of the vectors.
# The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a
# e_1^a &= \frac{v_1^a}{\omega_{11}} \\
# e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} \\
# e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, \\
# Normalize the first vector
w1U = ixp.zerorank1()
for a in range(DIM):
w1U[a] = v1U[a]
omega11 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega11 += w1U[a] * w1U[b] * gammaDD[a][b]
e1U = ixp.zerorank1()
for a in range(DIM):
e1U[a] = w1U[a] / sp.sqrt(omega11)
# Subtract off the portion of the first vector along the second, then normalize
omega12 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega12 += e1U[a] * v2U[b] * gammaDD[a][b]
w2U = ixp.zerorank1()
for a in range(DIM):
w2U[a] = v2U[a] - omega12*e1U[a]
omega22 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega22 += w2U[a] * w2U[b] *gammaDD[a][b]
e2U = ixp.zerorank1()
for a in range(DIM):
e2U[a] = w2U[a] / sp.sqrt(omega22)
# Subtract off the portion of the first and second vectors along the third, then normalize
omega13 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega13 += e1U[a] * v3U[b] * gammaDD[a][b]
omega23 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega23 += e2U[a] * v3U[b] * gammaDD[a][b]
w3U = ixp.zerorank1()
for a in range(DIM):
w3U[a] = v3U[a] - omega13*e1U[a] - omega23*e2U[a]
omega33 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega33 += w3U[a] * w3U[b] * gammaDD[a][b]
e3U = ixp.zerorank1()
for a in range(DIM):
e3U[a] = w3U[a] / sp.sqrt(omega33)
# Step 3c: Construct the tetrad itself.
# Eqs. 5.6:
# l^a &= \frac{1}{\sqrt{2}} e_2^a \\
# n^a &= -\frac{1}{\sqrt{2}} e_2^a \\
# m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\
# \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a)
isqrt2 = 1/sp.sqrt(2)
ltetU = ixp.zerorank1()
ntetU = ixp.zerorank1()
#mtetU = ixp.zerorank1()
#mtetccU = ixp.zerorank1()
remtetU = ixp.zerorank1() # SymPy does not like trying to take the real/imaginary parts of such a
immtetU = ixp.zerorank1() # complicated expression as the Weyl scalars, so we will do it ourselves.
for i in range(DIM):
ltetU[i] = isqrt2 * e2U[i]
ntetU[i] = -isqrt2 * e2U[i]
remtetU[i] = isqrt2 * e3U[i]
immtetU[i] = isqrt2 * e1U[i]
nn = isqrt2
else:
print("Error: TetradChoice == "+par.parval_from_str("TetradChoice")+" unsupported!")
exit(1)
gammaDD_dD = ixp.declarerank3("gammaDD_dD","sym01")
# Define the Christoffel symbols
GammaUDD = ixp.zerorank3(DIM)
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\
(gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m])
# Step 4b: Declare and construct the Riemann curvature tensor:
# R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac})
# + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac}
gammaDD_dDD = ixp.declarerank4("gammaDD_dDD","sym01_sym23")
RiemannDDDD = ixp.zerorank4()
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \
gammaDD_dDD[b][c][d][a] - \
gammaDD_dDD[a][c][b][d] - \
gammaDD_dDD[b][d][a][c]) / 2
for e in range(DIM):
for j in range(DIM):
RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \
gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c]
# Step 4c: We also need the extrinsic curvature tensor $K_{ij}$.
# In Cartesian coordinates, we already made the components gridfunctions.
# We will, however, need to calculate the trace of K seperately:
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j] * kDD[i][j]
# Step 5: Build the formula for \psi_4.
# Gauss equation: involving the Riemann tensor and extrinsic curvature.
# GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j}
GaussDDDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][l] + kDD[i][k]*kDD[l][j] - kDD[i][l]*kDD[k][j]
# Codazzi equation: involving partial derivatives of the extrinsic curvature.
# We will first need to declare derivatives of kDD
# CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p})
kDD_dD = ixp.declarerank3("kDD_dD","sym01")
CodazziDDD = ixp.zerorank3()
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l]
for p in range(DIM):
CodazziDDD[j][k][l] += GammaUDD[p][j][l]*kDD[k][p] - GammaUDD[p][j][k]*kDD[l][p]
# Another piece. While not associated with any particular equation,
# this is still useful for organizational purposes.
# RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\
# = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl}
RojoDD = ixp.zerorank2()
for j in range(DIM):
for l in range(DIM):
RojoDD[j][l] = trK*kDD[j][l]
for p in range(DIM):
for d in range(DIM):
RojoDD[j][l] += gammaUU[p][d]*RiemannDDDD[j][p][l][d] - kDD[j][p]*gammaUU[p][d]*kDD[d][l]
# Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}}
# and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations.
# We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063
# In terms of the above-defined quantites, the psis are defined as:
# \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\
# &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\
# &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}.
# \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\
# &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\
# \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\
# &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\
# \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\
# &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\
# \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\
# &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\
# &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\
psi4r = sp.sympify(0)
psi4i = sp.sympify(0)
psi3r = sp.sympify(0)
psi3i = sp.sympify(0)
psi2r = sp.sympify(0)
psi2i = sp.sympify(0)
psi1r = sp.sympify(0)
psi1i = sp.sympify(0)
psi0r = sp.sympify(0)
psi0i = sp.sympify(0)
for l in range(DIM):
for j in range(DIM):
psi4r += RojoDD[j][l] * nn * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r +=-RojoDD[j][l] * nn * nn * (ntetU[j]-ltetU[j]) * remtetU[l]
psi3i += RojoDD[j][l] * nn * nn * (ntetU[j]-ltetU[j]) * immtetU[l]
psi2r +=-RojoDD[j][l] * nn * nn * (remtetU[l]*remtetU[j]+immtetU[j]*immtetU[l])
psi2i +=-RojoDD[j][l] * nn * nn * (immtetU[l]*remtetU[j]-remtetU[j]*immtetU[l])
psi1r += RojoDD[j][l] * nn * nn * (ntetU[j]*remtetU[l]-ltetU[j]*remtetU[l])
psi1i += RojoDD[j][l] * nn * nn * (ntetU[j]*immtetU[l]-ltetU[j]*immtetU[l])
psi0r += RojoDD[j][l] * nn * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += RojoDD[j][l] * nn * nn * (remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
for l in range(DIM):
for j in range(DIM):
for k in range(DIM):
psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r += 1 * CodazziDDD[j][k][l] * nn * ((ntetU[j]-ltetU[j])*remtetU[k]*ntetU[l]-remtetU[j]*ltetU[k]*ntetU[l])
psi3i +=-1 * CodazziDDD[j][k][l] * nn * ((ntetU[j]-ltetU[j])*immtetU[k]*ntetU[l]-immtetU[j]*ltetU[k]*ntetU[l])
psi2r += 1 * CodazziDDD[j][k][l] * nn * (ntetU[l]*(remtetU[j]*remtetU[k]+immtetU[j]*immtetU[k])-ltetU[k]*(remtetU[j]*remtetU[l]+immtetU[j]*immtetU[l]))
psi2i += 1 * CodazziDDD[j][k][l] * nn * (ntetU[l]*(immtetU[j]*remtetU[k]-remtetU[j]*immtetU[k])-ltetU[k]*(remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l]))
psi1r += 1 * CodazziDDD[j][k][l] * nn * (ltetU[j]*remtetU[k]*ltetU[l]-remtetU[j]*ntetU[k]*ltetU[l]-ntetU[j]*remtetU[k]*ltetU[l])
psi1i += 1 * CodazziDDD[j][k][l] * nn * (ltetU[j]*immtetU[k]*ltetU[l]-immtetU[j]*ntetU[k]*ltetU[l]-ntetU[j]*immtetU[k]*ltetU[l])
psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k]*(remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k]*(remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
for l in range(DIM):
for j in range(DIM):
for k in range(DIM):
for i in range(DIM):
psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[j] * remtetU[k] * ntetU[l]
psi3i +=-GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[j] * immtetU[k] * ntetU[l]
psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * (remtetU[j]*remtetU[k]+immtetU[j]*immtetU[k])
psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * (immtetU[j]*remtetU[k]-remtetU[j]*immtetU[k])
psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[j] * remtetU[k] * ltetU[l]
psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[j] * immtetU[k] * ltetU[l]
psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * (remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
/* EVOLVED VARIABLES: */
#define NUM_AUX_GFS 15
#define KDD00GF 0
#define KDD01GF 1
#define KDD02GF 2
#define KDD11GF 3
#define KDD12GF 4
#define KDD22GF 5
#define GAMMADD00GF 6
#define GAMMADD01GF 7
#define GAMMADD02GF 8
#define GAMMADD11GF 9
#define GAMMADD12GF 10
#define GAMMADD22GF 11
#define XGF 12
#define YGF 13
#define ZGF 14
#define REAL double
#define NGHOSTS 2
// Cartesian coordinates parameters
const REAL xmin = -10.,xmax=10.;
const REAL ymin = -10.,ymax=10.;
const REAL zmin = -10.,zmax=10.;
#define IDX4(g,i,j,k) \
( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * ( (k) + Nxx_plus_2NGHOSTS[2] * (g) ) ) )
#define IDX3(i,j,k) ( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * (k) ) )
// Assuming idx = IDX3(i,j,k). Much faster if idx can be reused over and over:
#define IDX4pt(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2]) * (g) )
int main(int argc, const char *argv[]) {
// Step 0a: Read command-line input, error out if nonconformant
if((argc != 4) || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < 2 /* FIXME; allow for axisymmetric sims */) {
fprintf(stderr,"Error: Expected three command-line arguments: ./BrillLindquist_Playground Nx0 Nx1 Nx2,\n");
fprintf(stderr,"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.\n");
fprintf(stderr,"Nx[] MUST BE larger than NGHOSTS (= %d)\n",NGHOSTS);
exit(1);
}
// Step 0b: Set up numerical grid structure, first in space...
const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };
if(Nxx[0]%2 != 0 || Nxx[1]%2 != 0 || Nxx[2]%2 != 0) {
fprintf(stderr,"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.\n");
fprintf(stderr," For example, in case of angular directions, proper symmetry zones will not exist.\n");
exit(1);
}
const int Nxx_plus_2NGHOSTS[3] = { Nxx[0]+2*NGHOSTS, Nxx[1]+2*NGHOSTS, Nxx[2]+2*NGHOSTS };
const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2];
REAL *aux_gfs_new = (REAL *)malloc(sizeof(REAL) * NUM_AUX_GFS * Nxx_plus_2NGHOSTS_tot);
REAL *aux_gfs_old = (REAL *)malloc(sizeof(REAL) * NUM_AUX_GFS * Nxx_plus_2NGHOSTS_tot);
// Step 0d: Set up space and time coordinates
// Step 0d.i: Set \Delta x^i on uniform grids.
REAL xxmin[3],xxmax[3];
xxmin[0] = xmin;
xxmin[1] = ymin;
xxmin[2] = zmin;
xxmax[0] = xmax;
xxmax[1] = ymax;
xxmax[2] = zmax;
REAL dxx[3];
for(int i=0;i<3;i++) dxx[i] = (xxmax[i] - xxmin[i]) / ((REAL)Nxx[i]);
// Step 0d.ii: Set up uniform coordinate grids
REAL *xx[3];
for(int i=0;i<3;i++) {
xx[i] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS[i]);
for(int j=0;j<Nxx_plus_2NGHOSTS[i];j++) {
xx[i][j] = xxmin[i] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*dxx[i]; // Cell-centered grid.
}
}
struct timeval time;
gettimeofday(&time,NULL);
// microsecond has 1 000 000
// Assuming you did not need quite that accuracy
// Also do not assume the system clock has that accuracy.
srand48((time.tv_sec * 1000) + (time.tv_usec / 1000));
/*
time_t t;
srand48((unsigned) time(&t));
*/
#define PERT_SIZE 1e-6
#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) \
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)
LOOP_REGION(0,Nxx_plus_2NGHOSTS[0], 0,Nxx_plus_2NGHOSTS[1], 0,Nxx_plus_2NGHOSTS[2]) {
for(int gf=KDD00GF;gf<=KDD22GF;gf++) aux_gfs_new[IDX4(gf,i0,i1,i2)] = aux_gfs_old[IDX4(gf,i0,i1,i2)] = PERT_SIZE*(0.5-drand48());
for(int gf=GAMMADD00GF;gf<=GAMMADD22GF;gf++) aux_gfs_new[IDX4(gf,i0,i1,i2)] = aux_gfs_old[IDX4(gf,i0,i1,i2)] = PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD00GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD00GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD11GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD11GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD22GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD22GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(XGF,i0,i1,i2)] = aux_gfs_old[IDX4(XGF,i0,i1,i2)] = xx[0][i0];
aux_gfs_new[IDX4(YGF,i0,i1,i2)] = aux_gfs_old[IDX4(YGF,i0,i1,i2)] = xx[1][i1];
aux_gfs_new[IDX4(ZGF,i0,i1,i2)] = aux_gfs_old[IDX4(ZGF,i0,i1,i2)] = xx[2][i2];
}
REAL psi4_real_new,psi4_real_old;
LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS[0]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[1]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[2]-NGHOSTS) {
REAL *aux_gfs = aux_gfs_new;
const REAL xx0 = xx[0][i0], xx1 = xx[1][i1], xx2 = xx[2][i2];
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
REAL psi4_real;
#include "Psi4_new.h"
if(i0==Nxx_plus_2NGHOSTS[0]/2 && i1==Nxx_plus_2NGHOSTS[1]/2 && i2==Nxx_plus_2NGHOSTS[2]/2) {
//printf("%.15e\n",psi4_real);
psi4_real_new = psi4_real;
}
//printf("%d %d %d %.15e\n",i0,i1,i2,psi4_real);
}
LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS[0]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[1]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[2]-NGHOSTS) {
REAL *aux_gfs = aux_gfs_old;
const REAL xx0 = xx[0][i0], xx1 = xx[1][i1], xx2 = xx[2][i2];
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
REAL psi4_real;
#include "Psi4_old.h"
if(i0==Nxx_plus_2NGHOSTS[0]/2 && i1==Nxx_plus_2NGHOSTS[1]/2 && i2==Nxx_plus_2NGHOSTS[2]/2) {
psi4_real_old = psi4_real;
//printf("%.15e\n",psi4_real);
}
}
REAL relerror = fabs(psi4_real_new - psi4_real_old) / (0.5* ( fabs(psi4_real_new) + fabs(psi4_real_old) ));
if(relerror < 1e-13) {
printf("GOOD: %.15e : %.15e %.15e\n",relerror, psi4_real_new,psi4_real_old);
} else {
printf("BAD: %.15e : %.15e %.15e %ld\n",relerror, psi4_real_new,psi4_real_old, (time.tv_sec * 1000) + (time.tv_usec / 1000));
}
for(int i=0;i<3;i++) free(xx[i]);
free(aux_gfs_new);
free(aux_gfs_old);
return 0;
}
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4_tetrads.ipynb,
# this module will construct tetrads
# needed to compute \psi_4 (as well as other
# Weyl scalars and invariants in principle)
# Authors: Zachariah B. Etienne
# (zachetie **at** gmail **dot* com),
# and Patrick Nelson
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
# Step 1.b: Initialize TetradChoice parameter
thismodule = __name__
# Current option: QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char", thismodule, "TetradChoice", "QuasiKinnersley"))
par.initialize_param(par.glb_param("char", thismodule, "UseCorrectUnitNormal", "False")) # False = consistent with WeylScal4 ETK thorn.
def Psi4_tetrads():
global l4U, n4U, mre4U, mim4U
# Step 1.c: Check if tetrad choice is implemented:
if par.parval_from_str(thismodule+"::TetradChoice") != "QuasiKinnersley":
print("ERROR: "+thismodule+"::TetradChoice = "+par.parval_from_str("TetradChoice")+" currently unsupported!")
sys.exit(1)
# Step 1.d: Given the chosen coordinate system, set up
# corresponding reference metric and needed
# reference metric quantities
# The following function call sets up the reference metric
# and related quantities, including rescaling matrices ReDD,
# ReU, and hatted quantities.
rfm.reference_metric()
# Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is
# a 3+1-dimensional decomposition of the general
# relativistic field equations)
DIM = 3
# Step 1.f: Import all ADM quantities as written in terms of BSSN quantities
import BSSN.ADM_in_terms_of_BSSN as AB
AB.ADM_in_terms_of_BSSN()
# Step 2.a: Declare the Cartesian x,y,z in terms of
# xx0,xx1,xx2.
x = rfm.xxCart[0]
y = rfm.xxCart[1]
z = rfm.xxCart[2]
# Step 2.b: Declare v_1^a, v_2^a, and v_3^a tetrads,
# as well as detgamma and gammaUU from
# BSSN.ADM_in_terms_of_BSSN
v1UCart = ixp.zerorank1()
v2UCart = ixp.zerorank1()
detgamma = AB.detgamma
gammaUU = AB.gammaUU
# Step 2.c: Define v1U and v2U
v1UCart = [-y, x, sp.sympify(0)]
v2UCart = [x, y, z]
# Step 2.d: Construct the Jacobian d x_Cart^i / d xx^j
Jac_dUCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUCart_dDrfmUD[i][j] = sp.simplify(sp.diff(rfm.xxCart[i], rfm.xx[j]))
# Step 2.e: Invert above Jacobian to get needed d xx^j / d x_Cart^i
Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUCart_dDrfmUD)
# Step 2.e.i: Simplify expressions for d xx^j / d x_Cart^i:
for i in range(DIM):
for j in range(DIM):
Jac_dUrfm_dDCartUD[i][j] = sp.simplify(Jac_dUrfm_dDCartUD[i][j])
# Step 2.f: Transform v1U and v2U from the Cartesian to the xx^i basis
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j]
v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j]
# Step 2.g: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 2.h: Define v3U
v3U = ixp.zerorank1()
LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3)
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]
# Step 2.h.i: Simplify expressions for v1U,v2U,v3U. This greatly expedites the C code generation (~10x faster)
for a in range(DIM):
v1U[a] = sp.simplify(v1U[a])
v2U[a] = sp.simplify(v2U[a])
v3U[a] = sp.simplify(v3U[a])
# Step 2.i: Define omega_{ij}
omegaDD = ixp.zerorank2()
gammaDD = AB.gammaDD
def v_vectorDU(v1U,v2U,v3U, i,a):
if i==0:
return v1U[a]
elif i==1:
return v2U[a]
elif i==2:
return v3U[a]
else:
print("ERROR: unknown vector!")
sys.exit(1)
def update_omega(omegaDD, i,j, v1U,v2U,v3U,gammaDD):
omegaDD[i][j] = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omegaDD[i][j] += v_vectorDU(v1U,v2U,v3U, i,a)*v_vectorDU(v1U,v2U,v3U, j,b)*gammaDD[a][b]
# Step 2.j: Define e^a_i. Note that:
# omegaDD[0][0] = \omega_{11} above;
# omegaDD[1][1] = \omega_{22} above, etc.
# First e_1^a: Orthogonalize & normalize:
e1U = ixp.zerorank1()
update_omega(omegaDD, 0,0, v1U,v2U,v3U,gammaDD)
for a in range(DIM):
e1U[a] = v1U[a]/sp.sqrt(omegaDD[0][0])
# Next e_2^a: First orthogonalize:
e2U = ixp.zerorank1()
update_omega(omegaDD, 0,1, e1U,v2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] = (v2U[a] - omegaDD[0][1]*e1U[a])
# Then normalize:
update_omega(omegaDD, 1,1, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] /= sp.sqrt(omegaDD[1][1])
# Next e_3^a: First orthogonalize:
e3U = ixp.zerorank1()
update_omega(omegaDD, 0,2, e1U,e2U,v3U,gammaDD)
update_omega(omegaDD, 1,2, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e3U[a] = (v3U[a] - omegaDD[0][2]*e1U[a] - omegaDD[1][2]*e2U[a])
# Then normalize:
update_omega(omegaDD, 2,2, e1U,e2U,e3U,gammaDD)
for a in range(DIM):
e3U[a] /= sp.sqrt(omegaDD[2][2])
# Step 2.k: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
r4U = ixp.zerorank1(DIM=4)
u4U = ixp.zerorank1(DIM=4)
theta4U = ixp.zerorank1(DIM=4)
phi4U = ixp.zerorank1(DIM=4)
for a in range(DIM):
r4U[ a+1] = e2U[a]
theta4U[a+1] = e3U[a]
phi4U[ a+1] = e1U[a]
# FIXME? assumes alpha=1, beta^i = 0
if par.parval_from_str(thismodule+"::UseCorrectUnitNormal") == "False":
u4U[0] = 1
else:
# Eq. 2.116 in Baumgarte & Shapiro:
# n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1.
import BSSN.BSSN_quantities as Bq
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
Bq.BSSN_basic_tensors()
u4U[0] = 1/Bq.alpha
for i in range(DIM):
u4U[i+1] = -Bq.betaU[i]/Bq.alpha
l4U = ixp.zerorank1(DIM=4)
n4U = ixp.zerorank1(DIM=4)
mre4U = ixp.zerorank1(DIM=4)
mim4U = ixp.zerorank1(DIM=4)
# M_SQRT1_2 = 1 / sqrt(2) (defined in math.h on Linux)
M_SQRT1_2 = par.Cparameters("#define",thismodule,"M_SQRT1_2","")
isqrt2 = M_SQRT1_2 #1/sp.sqrt(2) <- SymPy drops precision to 15 sig. digits in unit tests
for mu in range(4):
l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])
n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])
mre4U[mu] = isqrt2*theta4U[mu]
mim4U[mu] = isqrt2* phi4U[mu]
# This module sets up Shifted Kerr-Schild initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: George Vopal, gvopal **at** gmail **dot** com
# Zachariah B. Etienne, zachetie **at** gmail **dot** com
# ### NRPy+ Source Code for this module: [BSSN/ShiftedKerrSchild.py](../edit/BSSN/BrillLindquist.py)
#
# WARNING: This module has not yet undergone code testing.
# **Inputs for initial data**:
#
# * The black hole mass, M
# * The black hole spin parameter, a
# Step 1: Initialize core Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB
thismodule = __name__
# Input parameters:
M, a, r0 = par.Cparameters("REAL", thismodule,
["M", "a", "r0"],
[1.0, 0.9, 1.0])
# ComputeADMGlobalsOnly == True will only set up the ADM global quantities.
# == False will perform the full ADM SphorCart->BSSN Curvi conversion
def ShiftedKerrSchild(ComputeADMGlobalsOnly = False):
global Sph_r_th_ph,r,th,ph, rho2, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU
# All gridfunctions will be written in terms of spherical coordinates (r, th, ph):
r,th,ph = sp.symbols('r th ph', real=True)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
# Auxiliary variables:
rho2 = sp.symbols('rho2', real=True)
# Step 1: Define rho^2, alpha, beta^(r_{KS}), beta^(theta), beta^(phi), gamma_{r_{KS}theta}, gamma_{theta\phi}
# r_{KS} = r + r0
rKS = r+r0
# rho^2 = rKS^2 + a^2*cos^2(theta)
rho2 = rKS*rKS + a*a*sp.cos(th)**2
# alpha = 1/sqrt{1 + M*rKS/rho^2}
alphaSph = 1/(sp.sqrt(1 + 2*M*rKS/rho2))
# Initialize the shift vector, \beta^i, to zero.
betaSphU = ixp.zerorank1()
# beta^r = alpha^2*2Mr/rho^2
betaSphU[0] = alphaSph*alphaSph*2*M*rKS/rho2
# Time derivative of shift vector beta^i, B^i, is zero.
BSphU = ixp.zerorank1()
# Step 2: Define and construct nonzero components gamma_{r_{KS}r_{KS}}$, gamma_{r_{KS}phi},
# gamma_{thetatheta}, gamma_{phiphi}
# Initialize \gamma_{ij} to zero.
gammaSphDD = ixp.zerorank2()
# gammaDD{rKS rKS} = 1 +2M*rKS/rho^2
gammaSphDD[0][0] = 1 + 2*M*rKS/rho2
# gammaDD{rKS phi} = -a*gammaDD{r r}*sin^2(theta)
gammaSphDD[0][2] = gammaSphDD[2][0] = -a*gammaSphDD[0][0]*sp.sin(th)**2
# gammaDD{theta theta} = rho^2
gammaSphDD[1][1] = rho2
# gammaDD{phi phi} = (rKS^2 + a^2 + 2Mr/rho^2*a^2*sin^2(theta))*sin^2(theta)
gammaSphDD[2][2] = (rKS*rKS + a*a + 2*M*rKS*a*a*sp.sin(th)**2/rho2)*sp.sin(th)**2
# Step 3: Define useful quantities A, B, C
# A = (a^2*cos^2(2theta) + a^2 + 2r^2)
A = (a*a*sp.cos(2*th) + a*a + 2*rKS*rKS)
# B = A + 4M*rKS
B = A + 4*M*rKS
# D = \sqrt(2M*rKS/(a^2cos^2(theta) + rKS^2) + 1)
D = sp.sqrt(2*M*rKS/(a*a*sp.cos(th)**2 + rKS*rKS) + 1)
# Step 4: Define the extrinsic curvature in spherical polar coordinates
# Establish the 3x3 zero-matrix
KSphDD = ixp.zerorank2()
# *** Fill in the nonzero components ***
# *** This will create an upper-triangular matrix ***
# K_{r r} = D(A+2Mr)/(A^2*B)[4M(a^2*cos(2theta) + a^2 - 2r^2)]
KSphDD[0][0] = D*(A+2*M*rKS)/(A*A*B)*(4*M*(a*a*sp.cos(2*th)+a*a-2*rKS*rKS))
# K_{r theta} = D/(AB)[8a^2*Mr*sin(theta)cos(theta)]
KSphDD[0][1] = KSphDD[1][0] = D/(A*B)*(8*a*a*M*rKS*sp.sin(th)*sp.cos(th))
# K_{r phi} = D/A^2[-2aMsin^2(theta)(a^2cos(2theta)+a^2-2r^2)]
KSphDD[0][2] = KSphDD[2][0] = D/(A*A)*(-2*a*M*sp.sin(th)**2*(a*a*sp.cos(2*th)+a*a-2*rKS*rKS))
# K_{theta theta} = D/B[4Mr^2]
KSphDD[1][1] = D/B*(4*M*rKS*rKS)
# K_{theta phi} = D/(AB)*(-8*a^3*Mr*sin^3(theta)cos(theta))
KSphDD[1][2] = KSphDD[2][1] = D/(A*B)*(-8*a**3*M*rKS*sp.sin(th)**3*sp.cos(th))
# K_{phi phi} = D/(A^2*B)[2Mr*sin^2(theta)(a^4(M+3r)
# +4a^2r^2(2r-M)+4a^2r*cos(2theta)(a^2+r(M+2r))+8r^5)]
KSphDD[2][2] = D/(A*A*B)*(2*M*rKS*sp.sin(th)**2*(a**4*(rKS-M)*sp.cos(4*th)\
+ a**4*(M+3*rKS)+4*a*a*rKS*rKS*(2*rKS-M)\
+ 4*a*a*rKS*sp.cos(2*th)*(a*a + rKS*(M + 2*rKS)) + 8*rKS**5))
if ComputeADMGlobalsOnly == True:
return
# Validated against original SENR:
#print(sp.mathematica_code(gammaSphDD[1][1]))
Sph_r_th_ph = [r,th,ph]
cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \
AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical", Sph_r_th_ph,
gammaSphDD,KSphDD,alphaSph,betaSphU,BSphU)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module sets up Static Trumpet initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: Terrence Pierre Jacques, terrencepierrej **at** gmail **dot** com
# Zachariah B. Etienne, zachetie **at** gmail **dot** com
# Ian Ruchlin
# ## This module sets up initial data for a static trumpet geometry in spherical coordinates. We can convert from spherical to any coordinate system defined in [reference_metric.py](../edit/reference_metric.py) (e.g., SinhSpherical, Cylindrical, Cartesian, etc.) using the [Exact ADM Spherical-to-BSSNCurvilinear converter module](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb)
#
# ### NRPy+ Source Code for this module: [BSSN/BrillLindquist.py](../edit/BSSN/BrillLindquist.py)
#
# <font color='green'>**All quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).**</font>
# ### Here we set up Static Trumpet initial data ([Dennison and Baumgarte, 2014](https://arxiv.org/abs/1403.5484)):
#
# Description of Static Trumpet geometry.
#
# **Inputs for initial data**:
#
# * The black hole mass $M$.
#
# **Additional variables needed for spacetime evolution**:
#
# * Desired coordinate system
# * Desired initial lapse $\alpha$ and shift $\beta^i$. We will choose our gauge conditions as $\alpha=1$ and $\beta^i=B^i=0$. $\alpha = \psi^{-2}$ will yield much better behavior, but the conformal factor $\psi$ depends on the desired *destination* coordinate system (which may not be spherical coordinates).
# Step 1: Initialize core Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB
thismodule = __name__
# Input parameters:
M = par.Cparameters("REAL", thismodule, ["M"], [1.0])
# ComputeADMGlobalsOnly == True will only set up the ADM global quantities.
# == False will perform the full ADM SphorCart->BSSN Curvi conversion
def StaticTrumpet(ComputeADMGlobalsOnly = False):
global Sph_r_th_ph,r,th,ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU
# All gridfunctions will be written in terms of spherical coordinates (r, th, ph):
r,th,ph = sp.symbols('r th ph', real=True)
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
# Step 1: Set psi, the conformal factor:
# Auxiliary variables:
psi0 = sp.symbols('psi0', real=True)
# *** The StaticTrumpet conformal factor ***
# Dennison and Baumgarte (2014) Eq. 13
# https://arxiv.org/pdf/1403.5484.pdf
# psi = sqrt{1 + M/r }
psi0 = sp.sqrt(1 + M/r)
# *** The physical spatial metric in spherical basis ***
# Set the upper-triangle of the matrix...
# Eq. 15
# gamma_{ij} = psi^4 * eta_{ij}
# eta_00 = 1, eta_11 = r^2, eta_22 = r^2 * sin^2 (theta)
gammaSphDD = ixp.zerorank2()
gammaSphDD[0][0] = psi0**4
gammaSphDD[1][1] = psi0**4 * r**2
gammaSphDD[2][2] = psi0**4 * r**2*sp.sin(th)**2
# ... then apply symmetries to get the other components
# *** The physical trace-free extrinsic curvature in spherical basis ***
# Set the upper-triangle of the matrix...
# Eq.19 and 20
KSphDD = ixp.zerorank2()
# K_{rr} = M / r^2
KSphDD[0][0] = -M / r**2
# K_{theta theta} = K_{phi phi} / sin^2 theta = M
KSphDD[1][1] = M
KSphDD[2][2] = M * sp.sin(th)**2
# ... then apply symmetries to get the other components
# Lapse function and shift vector
# Eq. 15
# alpha = r / (r+M)
alphaSph = r / (r + M)
betaSphU = ixp.zerorank1()
# beta^r = Mr / (r + M)^2
betaSphU[0] = M*r / (r + M)**2
BSphU = ixp.zerorank1()
if ComputeADMGlobalsOnly == True:
return
# Validated against original SENR:
#print(sp.mathematica_code(gammaSphDD[1][1]))
Sph_r_th_ph = [r,th,ph]
cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \
AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical", Sph_r_th_ph,
gammaSphDD,KSphDD,alphaSph,betaSphU,BSphU)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module performs the conversion of T^{mu nu}
# in Spherical or Cartesian coordinates
# given as *numerical* expressions (i.e., given as
# numerical values with fixed floating-point precision;
# e.g., in the case of an initial data solver), to
# rescaled BSSN stress-energy source terms.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: Import needed modules
from outputC import * # NRPy+: Core C code output module
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import BSSN.BSSN_quantities as Bq # NRPy+: This module depends on the parameter EvolvedConformalFactor_cf,
# which is defined in BSSN.BSSN_quantities
import sys # Standard Python modules for multiplatform OS-level functions
import BSSN.BSSN_RHSs as bssnrhs # NRPy+: BSSN RHS quantities
import loop as lp # NRPy+: Helper module for writing C-code loops
def T4UUmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear(CoordType_in,
Tmunu_input_function_name,
pointer_to_ID_inputs=False):
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift:
T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4)
gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
alphaSphorCart = sp.symbols("alphaSphorCart")
betaSphorCartU = ixp.declarerank1("betaSphorCartU")
# Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but
# in the Spherical or Cartesian basis.
# We first define the BSSN stress-energy source terms in the Spherical
# or Cartesian basis, respectively.
# To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S:
# S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
# S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu}
# S = \gamma^{ij} S_{ij}
# rho = n_\mu n_\nu T^{\mu\nu},
# where
# \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu
# and
# n_mu = {-\alpha,0,0,0},
# Step 2.1: Construct the 4-metric based on the input ADM quantities.
# This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf):
# g_{tt} = -\alpha^2 + \beta^k \beta_k
# g_{ti} = \beta_i
# g_{ij} = \gamma_{ij}
# Eq. 2.121 in B&S
betaSphorCartD = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j]
# Now compute the beta contraction.
beta2 = sp.sympify(0)
for i in range(DIM):
beta2 += betaSphorCartU[i] * betaSphorCartD[i]
# Eq. 2.122 in B&S
g4SphorCartDD = ixp.zerorank2(DIM=4)
g4SphorCartDD[0][0] = -alphaSphorCart ** 2 + beta2
for i in range(DIM):
g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i]
for i in range(DIM):
for j in range(DIM):
g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j]
# Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu:
n4SphorCartD = ixp.zerorank1(DIM=4)
n4SphorCartD[0] = -alphaSphorCart
gamma4SphorCartDD = ixp.zerorank2(DIM=4)
for mu in range(4):
for nu in range(4):
gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][nu] + n4SphorCartD[mu] * n4SphorCartD[nu]
# Step 2.3: We now have all we need to construct the BSSN source
# terms in the current basis (Spherical or Cartesian):
# S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
# S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu}
# S = \gamma^{ij} S_{ij}
# rho = n_\mu n_\nu T^{\mu\nu},
SSphorCartDD = ixp.zerorank2()
SSphorCartD = ixp.zerorank1()
SSphorCart = sp.sympify(0)
rhoSphorCart = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
for mu in range(4):
for nu in range(4):
SSphorCartDD[i][j] += gamma4SphorCartDD[i + 1][mu] * gamma4SphorCartDD[j + 1][nu] * T4SphorCartUU[mu][nu]
for i in range(DIM):
for mu in range(4):
for nu in range(4):
SSphorCartD[i] += -gamma4SphorCartDD[i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu]
gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
for i in range(DIM):
for j in range(DIM):
SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j]
for mu in range(4):
for nu in range(4):
rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu]
# Step 3: Perform basis conversion to
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
# Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
r_th_ph_or_Cart_xyz_oID_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
# Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaSphorCartDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
# basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
# we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
# Here we convert ADM quantities to their BSSN Curvilinear counterparts:
# Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$:
# We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
# \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}.
gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
gammabarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
gammabarDD[i][j] = (rfm.detgammahat / gammaDET) ** (sp.Rational(1, 3)) * gammaSphorCartDD[i][j]
# Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic
# curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$,
# where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):
# K = \gamma^{ij} K_{ij}, and
# \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right)
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaSphorCartUU[i][j] * KDD[i][j]
AbarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
AbarDD[i][j] = (rfm.detgammahat / gammaDET) ** (sp.Rational(1, 3)) * (
KDD[i][j] - sp.Rational(1, 3) * gammaSphorCartDD[i][j] * trK)
# Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set
# by the "BSSN_RHSs::EvolvedConformalFactor_cf" parameter. For example if
# "EvolvedConformalFactor_cf" is set to "phi", we can use Eq. 3 of
# [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf),
# which in arbitrary coordinates is written:
# \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right).
# Alternatively if "BSSN_RHSs::EvolvedConformalFactor_cf" is set to "chi", then
# \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right)
# = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}.
#
# Finally if "BSSN_RHSs::EvolvedConformalFactor_cf" is set to "W", then
# W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
# \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
# \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}.
# First compute gammabarDET:
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
cf = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 3))
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 6))
else:
print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
sys.exit(1)
# Step 4: Rescale tensorial quantities according to the prescription described in
# the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb)
# (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
#
# h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\
# a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\
# \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\
# \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\
# \mathcal{B}^i &= B^i/\text{ReU[i]}\\
hDD = ixp.zerorank2()
aDD = ixp.zerorank2()
vetU = ixp.zerorank1()
betU = ixp.zerorank1()
for i in range(DIM):
vetU[i] = betaU[i] / rfm.ReU[i]
betU[i] = BU[i] / rfm.ReU[i]
for j in range(DIM):
hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
# Step 5: Output all ADM-to-BSSN expressions to a C function. This function
# must first call the ID_ADM_SphorCart() defined above. Using these
# Spherical or Cartesian data, it sets up all quantities needed for
# BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
# computed from numerical data using finite-difference derivatives.
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "w") as file:
file.write("void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3],")
if pointer_to_ID_inputs == True:
file.write("ID_inputs *other_inputs,")
else:
file.write("ID_inputs other_inputs,")
file.write("""
REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
REAL *trK,
REAL *vetU0,REAL *vetU1,REAL *vetU2,
REAL *betU0,REAL *betU1,REAL *betU2,
REAL *alpha, REAL *cf) {
REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
const REAL xx0 = xx0xx1xx2[0];
const REAL xx1 = xx0xx1xx2[1];
const REAL xx2 = xx0xx1xx2[2];
REAL xyz_or_rthph[3];\n""")
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
"BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", outCparams + ",CSE_enable=False")
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file:
file.write(" "+ADM_input_function_name+"""(xyz_or_rthph, other_inputs,
&gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
&gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
&KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
&KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
&alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
&BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
// Next compute all rescaled BSSN curvilinear quantities:\n""")
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2],
trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2],
alpha, cf],
["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22",
"*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22",
"*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2",
"*alpha", "*cf"],
"BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", params=outCparams)
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file:
file.write("}\n")
# Step 5.A: Output the driver function for the above
# function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
# Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file:
file.write("void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],")
if pointer_to_ID_inputs == True:
file.write("ID_inputs *other_inputs,")
else:
file.write("ID_inputs other_inputs,")
file.write("REAL *in_gfs) {\n")
file.write(lp.loop(["i2", "i1", "i0"], ["0", "0", "0"],
["Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]"],
["1", "1", "1"], ["#pragma omp parallel for",
" const REAL xx2 = xx[2][i2];",
" const REAL xx1 = xx[1][i1];"], "",
"""const REAL xx0 = xx[0][i0];
const int idx = IDX3(i0,i1,i2);
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs,
&in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
&in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
&in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
&in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
&in_gfs[IDX4pt(TRKGF,idx)],
&in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
&in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
&in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
"""))
file.write("}\n")
# Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
# [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
# from finite-difference derivatives of rescaled metric
# quantities $h_{ij}$:
# \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).
# The reference_metric.py module provides us with analytic expressions for
# $\hat{\Gamma}^i_{jk}$, so here we need only compute
# finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
# the values for $h_{ij}$ provided in the initial data. Once
# $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
# procedure:
# \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},
# and then output the result to a C file using the NRPy+
# finite-difference C output routine.
# We will need all BSSN gridfunctions to be defined, as well as
# expressions for gammabarDD_dD in terms of exact derivatives of
# the rescaling matrix and finite-difference derivatives of
# hDD's.
gammabarDD = bssnrhs.gammabarDD
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
gammabarDD_dD = bssnrhs.gammabarDD_dD
# Next compute Christoffel symbols \bar{\Gamma}^i_{jk}:
GammabarUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammabarUDD[i][j][k] += sp.Rational(1, 2) * gammabarUU[i][l] * (gammabarDD_dD[l][j][k] +
gammabarDD_dD[l][k][j] -
gammabarDD_dD[j][k][l])
# Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD
# (from the reference metric):
LambdabarU = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k])
# Finally apply rescaling:
# lambda^i = Lambdabar^i/\text{ReU[i]}
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])]
lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams)
with open("BSSN/ID_BSSN_lambdas.h", "w") as file:
file.write("""
void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n""")
file.write(lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"],
["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"],
["1", "1", "1"], ["const REAL invdx0 = 1.0/dxx[0];\n" +
"const REAL invdx1 = 1.0/dxx[1];\n" +
"const REAL invdx2 = 1.0/dxx[2];\n" +
"#pragma omp parallel for",
" const REAL xx2 = xx[2][i2];",
" const REAL xx1 = xx[1][i1];"], "",
"const REAL xx0 = xx[0][i0];\n" + lambdaU_expressions_FDout))
file.write("}\n")
# This module sets up UIUC Black Hole initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: Zachariah B. Etienne, zachetie **at** gmail **dot** com
# Terrence Pierre Jacques, terrencepierrej **at** gmail **dot** com
# Ian Ruchlin
# ## This module sets up initial data for a merging black hole system in spherical coordinates. We can convert from spherical to any coordinate system defined in [reference_metric.py](../edit/reference_metric.py) (e.g., SinhSpherical, Cylindrical, Cartesian, etc.) using the [Exact ADM Spherical-to-BSSNCurvilinear converter module](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb)
#
# ### Here we set up UIUC Black Hole initial data ([Liu, Etienne, & Shapiro, PRD 80 121503, 2009](https://arxiv.org/abs/1001.4077)):
#
# UIUC black holes have the advantage of finite coordinate radius in the maximal spin limit. It is therefore excellent for studying very highly spinning black holes. This module sets the UIUC black hole at the origin.
#
# **Inputs for initial data**:
#
# * The black hole mass $M$.
# * The dimensionless spin parameter $\chi = a/M$
#
# **Additional variables needed for spacetime evolution**:
#
# * Desired coordinate system
# * Desired initial lapse $\alpha$ and shift $\beta^i$. We will choose our gauge conditions as $\alpha=1$ and $\beta^i=B^i=0$. $\alpha = \psi^{-2}$ will yield much better behavior, but the conformal factor $\psi$ depends on the desired *destination* coordinate system (which may not be spherical coordinates).
# Step P0: Load needed modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB
thismodule = __name__
# The UIUC initial data represent a Kerr black hole with mass M
# and dimensionless spin chi in UIUC quasi-isotropic coordinates,
# see https://arxiv.org/abs/1001.4077
# Input parameters:
M, chi = par.Cparameters("REAL", thismodule, ["M", "chi"], [1.0, 0.99])
# ComputeADMGlobalsOnly == True will only set up the ADM global quantities.
# == False will perform the full ADM SphorCart->BSSN Curvi conversion
def UIUCBlackHole(ComputeADMGlobalsOnly = False):
global Sph_r_th_ph,r,th,ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU
# All gridfunctions will be written in terms of spherical coordinates (r, th, ph):
r,th,ph = sp.symbols('r th ph', real=True)
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
# Step 1: Set psi, the conformal factor:
# Spin per unit mass
a = M*chi
# Defined under equation 1 in Liu, Etienne, & Shapiro (2009) https://arxiv.org/pdf/1001.4077.pdf
# Boyer - Lindquist outer horizon
rp = M + sp.sqrt(M**2 - a**2)
# Boyer - Lindquist inner horizon
rm = M - sp.sqrt(M**2 - a**2)
# Boyer - Lindquist radius in terms of UIUC radius
# Eq. 11
# r_{BL} = r * ( 1 + r_+ / 4r )^2
rBL = r*(1 + rp / (4*r))**2
# Expressions found below Eq. 2
# Sigma = r_{BL}^2 + a^2 cos^2 theta
SIG = rBL**2 + a**2*sp.cos(th)**2
# Delta = r_{BL}^2 - 2Mr_{BL} + a^2
DEL = rBL**2 - 2*M*rBL + a**2
# A = (r_{BL}^2 + a^2)^2 - Delta a^2 sin^2 theta
AA = (rBL**2 + a**2)**2 - DEL*a**2*sp.sin(th)**2
# *** The ADM 3-metric in spherical basis ***
gammaSphDD = ixp.zerorank2()
# Declare the nonzero components of the 3-metric
# (Eq. 13 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf):
# ds^2 = Sigma (r + r_+/4)^2 / ( r^3 (r_{BL} - r_- ) * dr^2 +
# Sigma d theta^2 + (A sin^2 theta) / Sigma * d\phi^2
gammaSphDD[0][0] = ((SIG*(r + rp/4)**2)/(r**3*(rBL - rm)))
gammaSphDD[1][1] = SIG
gammaSphDD[2][2] = AA/SIG*sp.sin(th)**2
# *** The physical trace-free extrinsic curvature in spherical basis ***
# Nonzero components of the extrinsic curvature K, given by
# Eq. 14 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf:
KSphDD = ixp.zerorank2()
# K_{r phi} = K_{phi r} = (Ma sin^2 theta) / (Sigma sqrt{A Sigma}) *
# [3r^4_{BL} + 2a^2 r^2_{BL} - a^4 - a^2 (r^2_{BL} - a^2) sin^2 theta] *
# (1 + r_+ / 4r) (1 / sqrt{r(r_{BL} - r_-)})
KSphDD[0][2] = KSphDD[2][0] = (M*a*sp.sin(th)**2)/(SIG*sp.sqrt(AA*SIG))*\
(3*rBL**4 + 2*a**2*rBL**2 - a**4- a**2*(rBL**2 - a**2)*\
sp.sin(th)**2)*(1 + rp/(4*r))*1/sp.sqrt(r*(rBL - rm))
# Components of the extrinsic curvature K, given by
# Eq. 15 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf:
# K_{theta phi} = K_{phi theta} = -(2a^3 Mr_{BL} cos theta sin^3 theta) /
# (Sigma sqrt{A Sigma}) x (r - r_+ / 4) sqrt{(r_{BL} - r_-) / r }
KSphDD[1][2] = KSphDD[2][1] = -((2*a**3*M*rBL*sp.cos(th)*sp.sin(th)**3)/ \
(SIG*sp.sqrt(AA*SIG)))*(r - rp/4)*sp.sqrt((rBL - rm)/r)
alphaSph = sp.sympify(1) # We generally choose alpha = 1/psi**2 (psi = BSSN conformal factor) for these initial data
betaSphU = ixp.zerorank1() # We generally choose \beta^i = 0 for these initial data
BSphU = ixp.zerorank1() # We generally choose B^i = 0 for these initial data
if ComputeADMGlobalsOnly == True:
return
# Validated against original SENR: KSphDD[0][2], KSphDD[1][2], gammaSphDD[2][2], gammaSphDD[0][0], gammaSphDD[1][1]
#print(sp.mathematica_code(gammaSphDD[1][1]))
Sph_r_th_ph = [r,th,ph]
cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \
AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical", Sph_r_th_ph,
gammaSphDD,KSphDD,alphaSph,betaSphU,BSphU)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
from UnitTesting.create_test import create_test
def test_ADMBSSN_tofrom_4metric():
module = 'BSSN.ADMBSSN_tofrom_4metric'
module_name = 'ADMBSSN_tofrom_4metric'
function_and_global_dict = {'g4DD_ito_BSSN_or_ADM(\"ADM\")': ['g4DD'],
'g4UU_ito_BSSN_or_ADM(\"ADM\")': ['g4UU'],
'BSSN_or_ADM_ito_g4DD(\"ADM\")': ['gammaDD', 'betaU', 'alpha']}
create_test(module, module_name, function_and_global_dict)
def test_ADMBSSN_tofrom_4metric_BSSN():
module = 'BSSN.ADMBSSN_tofrom_4metric'
module_name = 'ADMBSSN_tofrom_4metric'
function_and_global_dict = {'g4DD_ito_BSSN_or_ADM(\"BSSN\")': ['g4DD'],
'g4UU_ito_BSSN_or_ADM(\"BSSN\")': ['g4UU'],
'BSSN_or_ADM_ito_g4DD(\"BSSN\")': ['hDD', 'cf', 'vetU', 'alpha']}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'g4DD_ito_BSSN_or_ADM(\"BSSN\")': rfm_init_string,
'g4UU_ito_BSSN_or_ADM(\"BSSN\")': rfm_init_string,
'BSSN_or_ADM_ito_g4DD(\"BSSN\")': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_BSSN_stress_energy_source_terms():
module = 'BSSN.BSSN_stress_energy_source_terms'
module_name = 'BSSN_stress_energy_source_terms'
function_and_global_dict = {'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"ADM\")': ['SDD','SD','S','rho'],
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"BSSN\")': ['SDD','SD','S','rho'],
'BSSN_source_terms_for_BSSN_RHSs()': ['sourceterm_trK_rhs', 'sourceterm_a_rhsDD', 'sourceterm_lambda_rhsU', 'sourceterm_Lambdabar_rhsU']}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"ADM\")': rfm_init_string,
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"BSSN\")': rfm_init_string,
'BSSN_source_terms_for_BSSN_RHSs()': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_ADM_in_terms_of_BSSN():
module = 'BSSN.ADM_in_terms_of_BSSN'
module_name = 'ADM_in_terms_of_BSSN'
function_and_global_dict = {'ADM_in_terms_of_BSSN()': ['gammaDD', 'gammaDDdD', 'gammaDDdDD', 'gammaUU', 'detgamma',
'GammaUDD', 'KDD', 'KDDdD']}
create_test(module, module_name, function_and_global_dict)
def test_BSSN_in_terms_of_ADM():
module = 'BSSN.BSSN_in_terms_of_ADM'
module_name = 'BSSN_in_terms_of_ADM'
function_and_global_dict = {'gammabarDD_hDD(gammaDD=None)': ['gammabarDD','hDD'],
'trK_AbarDD_aDD(gammaDD=None,KDD=None)': ['trK','AbarDD','aDD'],
'LambdabarU_lambdaU__exact_gammaDD(gammaDD=None)': ['LambdabarU','lambdaU'],
'cf_from_gammaDD(gammaDD=None)': ['cf'],
'betU_vetU(betaU=None,BU=None)': ['vetU','betU']
}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'gammabarDD_hDD(gammaDD=None)': rfm_init_string,
'trK_AbarDD_aDD(gammaDD=None,KDD=None)': rfm_init_string,
'LambdabarU_lambdaU__exact_gammaDD(gammaDD=None)': rfm_init_string,
'cf_from_gammaDD(gammaDD=None)': rfm_init_string,
'betU_vetU(betaU=None,BU=None)': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_BrillLindquist():
module = 'BSSN.BrillLindquist'
module_name = 'BrillLindquist'
function_and_global_dict = {'BrillLindquist(ComputeADMGlobalsOnly=True)': ['alphaCart', 'betaCartU', 'BCartU',
'gammaCartDD', 'KCartDD']}
create_test(module, module_name, function_and_global_dict)
def test_constraints():
module = 'BSSN.BSSN_constraints'
module_name = 'BSSN_constraints'
function_and_global_dict = {'BSSN_constraints()': ['H', 'MU']}
create_test(module, module_name, function_and_global_dict)
def test_Psi4():
module = 'BSSN.Psi4'
module_name = 'Psi4'
function_and_global_dict = {'Psi4(specify_tetrad=False)': ['psi4_re_pt', 'psi4_im_pt']}
create_test(module, module_name, function_and_global_dict)
def test_Psi4_tetrads():
module = 'BSSN.Psi4_tetrads'
module_name = 'Psi4_tetrads'
function_and_global_dict = {'Psi4_tetrads()': ['l4U', 'n4U', 'mre4U', 'mim4U']}
create_test(module, module_name, function_and_global_dict)
def test_quantities():
module = 'BSSN.BSSN_quantities'
module_name = 'BSSN_quantities'
function_and_global_dict = {'declare_BSSN_gridfunctions_if_not_declared_already()': ['hDD', 'aDD', 'lambdaU', 'vetU', 'betU', 'trK', 'cf', 'alpha'],
'BSSN_basic_tensors()': ['gammabarDD', 'AbarDD', 'LambdabarU', 'betaU', 'BU'],
'gammabar__inverse_and_derivs()': ['gammabarUU', 'gammabarDD_dD', 'gammabarDD_dupD', 'gammabarDD_dDD', 'GammabarUDD'],
'detgammabar_and_derivs()': ['detgammabar', 'detgammabar_dD', 'detgammabar_dDD'],
'AbarUU_AbarUD_trAbar_AbarDD_dD()': ['AbarUU', 'AbarUD', 'trAbar', 'AbarDD_dD', 'AbarDD_dupD'],
'RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()': ['RbarDD', 'DGammaUDD', 'gammabarDD_dHatD', 'DGammaU'],
'betaU_derivs()': ['betaU_dD', 'betaU_dupD', 'betaU_dDD'],
'phi_and_derivs()': ['phi_dD', 'phi_dupD', 'phi_dDD', 'exp_m4phi', 'phi_dBarD', 'phi_dBarDD']
}
rfm_init_string = '''
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'BSSN_basic_tensors()': rfm_init_string,
'gammabar__inverse_and_derivs()': rfm_init_string,
'detgammabar_and_derivs()': rfm_init_string,
'AbarUU_AbarUD_trAbar_AbarDD_dD()': rfm_init_string,
'RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()': rfm_init_string,
'betaU_derivs()': rfm_init_string,
'phi_and_derivs()': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_RHSs():
module = 'BSSN.BSSN_RHSs'
module_name = 'BSSN_RHSs'
function_and_global_dict = {'BSSN_RHSs()': ['cf_rhs', 'trK_rhs', 'lambda_rhsU', 'a_rhsDD', 'h_rhsDD']}
create_test(module, module_name, function_and_global_dict)
def test_gauge_RHSs():
module = 'BSSN.BSSN_gauge_RHSs'
module_name = 'gauge_RHSs'
function_and_global_dict = {'BSSN_gauge_RHSs()': ['alpha_rhs', 'bet_rhsU', 'vet_rhsU']}
bssn_rhs_init_string = '''
import BSSN.BSSN_RHSs as rhs
rhs.BSSN_RHSs()
'''
initialization_string_dict = { 'BSSN_gauge_RHSs()': bssn_rhs_init_string }
create_test(module, module_name, function_and_global_dict, initialization_string_dict=initialization_string_dict)
def test_ShiftedKerrSchild():
module = 'BSSN.ShiftedKerrSchild'
module_name = 'ShiftedKerrSchild'
function_and_global_dict = {'ShiftedKerrSchild(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
def test_StaticTrumpet():
module = 'BSSN.StaticTrumpet'
module_name = 'StaticTrumpet'
function_and_global_dict = {'StaticTrumpet(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
def test_T4UUmunu_vars():
module = 'BSSN.BSSN_T4UUmunu_vars'
module_name = 'T4UUmunu_vars'
function_and_global_dict = {'define_BSSN_T4UUmunu_rescaled_source_terms()': ['rho', 'S', 'sD', 'sDD']}
create_test(module, module_name, function_and_global_dict)
def test_UIUCBlackHole():
module = 'BSSN.UIUCBlackHole'
module_name = 'UIUCBlackHole'
function_and_global_dict = {'UIUCBlackHole(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
if __name__ == '__main__':
import sys
if len(sys.argv) <= 3:
failed_functions = []
for fun in dir():
if fun[0:5] == 'test_':
print('\nTesting ' + str(fun) + '...\n')
try:
exec(fun + '()')
except SystemExit:
failed_functions.append(fun)
if failed_functions != []:
import sys, os
with open(os.path.join('UnitTesting', 'failed_tests.txt'), 'a') as file:
for function in failed_functions:
file.write(sys.argv[0] + ': ' + str(function) + '\n')
exit(1)
else:
globals()[sys.argv[4]]()
from mpmath import mpf, mp, mpc
from UnitTesting.standard_constants import precision
mp.dps = precision
trusted_values_dict = {}
# Generated on: 2019-08-09
trusted_values_dict['ADM_in_terms_of_BSSN__ADM_in_terms_of_BSSN__globals'] = {'gammaDD[0][0]': mpf('8.84684305820134422150597858207921'), 'gammaDD[0][1]': mpf('3.350018689599008046165002833204'), 'gammaDD[0][2]': mpf('0.194795767378908960688399288371257'), 'gammaDD[1][0]': mpf('3.350018689599008046165002833204'), 'gammaDD[1][1]': mpf('1.92752387150314689143948714387062'), 'gammaDD[1][2]': mpf('0.617514215490828029162196536408385'), 'gammaDD[2][0]': mpf('0.194795767378908960688399288371257'), 'gammaDD[2][1]': mpf('0.617514215490828029162196536408385'), 'gammaDD[2][2]': mpf('0.440219517017598275317485123438443'), 'gammaDDdD[0][0][0]': mpf('-39.2181788667717063214934687853284'), 'gammaDDdD[0][0][1]': mpf('-43.5347678242827125179031665798782'), 'gammaDDdD[0][0][2]': mpf('-43.5448629253377323761214222661368'), 'gammaDDdD[0][1][0]': mpf('-3.82269337582904325002653701794443'), 'gammaDDdD[0][1][1]': mpf('-15.8371132982064951265352981022794'), 'gammaDDdD[0][1][2]': mpf('-13.0007465813467278947970267824292'), 'gammaDDdD[0][2][0]': mpf('-0.218013883809053119477949934634148'), 'gammaDDdD[0][2][1]': mpf('0.785829885135145904482804685445781'), 'gammaDDdD[0][2][2]': mpf('-0.581351677673311207621301361731265'), 'gammaDDdD[1][0][0]': mpf('-3.82269337582904325002653701794443'), 'gammaDDdD[1][0][1]': mpf('-15.8371132982064951265352981022794'), 'gammaDDdD[1][0][2]': mpf('-13.0007465813467278947970267824292'), 'gammaDDdD[1][1][0]': mpf('1.02077344056659026570129696849467'), 'gammaDDdD[1][1][1]': mpf('-9.20007419968800686893912169288767'), 'gammaDDdD[1][1][2]': mpf('-8.15422339233949609213151393845906'), 'gammaDDdD[1][2][0]': mpf('0.357104131772599372488808535542396'), 'gammaDDdD[1][2][1]': mpf('-1.65079628886562588602648974576529'), 'gammaDDdD[1][2][2]': mpf('-2.86344337379037596725739579107855'), 'gammaDDdD[2][0][0]': mpf('-0.218013883809053119477949934634148'), 'gammaDDdD[2][0][1]': mpf('0.785829885135145904482804685445781'), 'gammaDDdD[2][0][2]': mpf('-0.581351677673311207621301361731265'), 'gammaDDdD[2][1][0]': mpf('0.357104131772599372488808535542396'), 'gammaDDdD[2][1][1]': mpf('-1.65079628886562588602648974576529'), 'gammaDDdD[2][1][2]': mpf('-2.86344337379037596725739579107855'), 'gammaDDdD[2][2][0]': mpf('0.195001221087943638085807308270354'), 'gammaDDdD[2][2][1]': mpf('-0.410311324085430408983791626770926'), 'gammaDDdD[2][2][2]': mpf('-1.92276041454685421458861940256278'), 'gammaDDdDD[0][0][0][0]': mpf('225.398957517527156513326956764229'), 'gammaDDdDD[0][0][0][1]': mpf('288.140070211252996184124719294385'), 'gammaDDdDD[0][0][0][2]': mpf('281.623967129510091655495816474147'), 'gammaDDdDD[0][0][1][0]': mpf('288.140070211252996184124719294385'), 'gammaDDdDD[0][0][1][1]': mpf('337.948619849001134211864646627346'), 'gammaDDdDD[0][0][1][2]': mpf('322.481152355898457651315654723053'), 'gammaDDdDD[0][0][2][0]': mpf('281.623967129510091655495816474147'), 'gammaDDdDD[0][0][2][1]': mpf('322.481152355898457651315654723053'), 'gammaDDdDD[0][0][2][2]': mpf('293.564334950019576720032123379472'), 'gammaDDdDD[0][1][0][0]': mpf('3.64051177720082509234617335007875'), 'gammaDDdDD[0][1][0][1]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[0][1][0][2]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[0][1][1][0]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[0][1][1][1]': mpf('119.02655264345724986788778186216'), 'gammaDDdDD[0][1][1][2]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[0][1][2][0]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[0][1][2][1]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[0][1][2][2]': mpf('77.760445818040255191531604261198'), 'gammaDDdDD[0][2][0][0]': mpf('0.375532488603708956700281774274923'), 'gammaDDdDD[0][2][0][1]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[0][2][0][2]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[0][2][1][0]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[0][2][1][1]': mpf('-6.12617437316042868416067368255866'), 'gammaDDdDD[0][2][1][2]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[0][2][2][0]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[0][2][2][1]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[0][2][2][2]': mpf('2.73521309266304236084886214346027'), 'gammaDDdDD[1][0][0][0]': mpf('3.64051177720082509234617335007875'), 'gammaDDdDD[1][0][0][1]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[1][0][0][2]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[1][0][1][0]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[1][0][1][1]': mpf('119.02655264345724986788778186216'), 'gammaDDdDD[1][0][1][2]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[1][0][2][0]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[1][0][2][1]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[1][0][2][2]': mpf('77.760445818040255191531604261198'), 'gammaDDdDD[1][1][0][0]': mpf('-9.92106187379982562603610831325235'), 'gammaDDdDD[1][1][0][1]': mpf('15.825454310362637536048355653535'), 'gammaDDdDD[1][1][0][2]': mpf('14.4039518436611112501034205835319'), 'gammaDDdDD[1][1][1][0]': mpf('15.825454310362637536048355653535'), 'gammaDDdDD[1][1][1][1]': mpf('70.6517225129821789456736189152217'), 'gammaDDdDD[1][1][1][2]': mpf('61.9035794546980075683100782914809'), 'gammaDDdDD[1][1][2][0]': mpf('14.4039518436611112501034205835319'), 'gammaDDdDD[1][1][2][1]': mpf('61.9035794546980075683100782914809'), 'gammaDDdDD[1][1][2][2]': mpf('50.8579841651123380242828623654651'), 'gammaDDdDD[1][2][0][0]': mpf('-3.34089527571068728373797749836677'), 'gammaDDdDD[1][2][0][1]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[1][2][0][2]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[1][2][1][0]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[1][2][1][1]': mpf('9.66395575640457553089340537573125'), 'gammaDDdDD[1][2][1][2]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[1][2][2][0]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[1][2][2][1]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[1][2][2][2]': mpf('19.0650383126789942251109383834193'), 'gammaDDdDD[2][0][0][0]': mpf('0.375532488603708956700281774274923'), 'gammaDDdDD[2][0][0][1]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[2][0][0][2]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[2][0][1][0]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[2][0][1][1]': mpf('-6.12617437316042868416067368255866'), 'gammaDDdDD[2][0][1][2]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[2][0][2][0]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[2][0][2][1]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[2][0][2][2]': mpf('2.73521309266304236084886214346027'), 'gammaDDdDD[2][1][0][0]': mpf('-3.34089527571068728373797749836677'), 'gammaDDdDD[2][1][0][1]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[2][1][0][2]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[2][1][1][0]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[2][1][1][1]': mpf('9.66395575640457553089340537573125'), 'gammaDDdDD[2][1][1][2]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[2][1][2][0]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[2][1][2][1]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[2][1][2][2]': mpf('19.0650383126789942251109383834193'), 'gammaDDdDD[2][2][0][0]': mpf('-2.52353147414316377574820905559147'), 'gammaDDdDD[2][2][0][1]': mpf('4.53575664377867062493942188091689'), 'gammaDDdDD[2][2][0][2]': mpf('3.56377908834306629097560544110174'), 'gammaDDdDD[2][2][1][0]': mpf('4.5357566437786706249394218809232'), 'gammaDDdDD[2][2][1][1]': mpf('1.84054709164445852274067505481422'), 'gammaDDdDD[2][2][1][2]': mpf('7.16074940759453707759590964424542'), 'gammaDDdDD[2][2][2][0]': mpf('3.56377908834306629097560544110174'), 'gammaDDdDD[2][2][2][1]': mpf('7.16074940759453707759590964424542'), 'gammaDDdDD[2][2][2][2]': mpf('12.0437865836043923259359609593463'), 'gammaUU[0][0]': mpf('-6.28938129956539035233976956863059'), 'gammaUU[0][1]': mpf('18.2330938347990520861338232067731'), 'gammaUU[0][2]': mpf('-22.7932869645533398576110813493934'), 'gammaUU[1][0]': mpf('18.2330938347990520861338232067731'), 'gammaUU[1][1]': mpf('-51.9160225808937703970803496330078'), 'gammaUU[1][2]': mpf('64.7566755861571412059499249935555'), 'gammaUU[2][0]': mpf('-22.7932869645533398576110813493934'), 'gammaUU[2][1]': mpf('64.7566755861571412059499249935555'), 'gammaUU[2][2]': mpf('-78.479328065890663873188546288808'), 'detgamma': mpf('-0.0742854979156755661927481235463981'), 'GammaUDD[0][0][0]': mpf('-40.7797584599095266200402578142104'), 'GammaUDD[0][0][1]': mpf('-14.9811790250568026018422503217171'), 'GammaUDD[0][0][2]': mpf('12.2823492767347497337333129446469'), 'GammaUDD[0][1][0]': mpf('-14.9811790250568026018422503217171'), 'GammaUDD[0][1][1]': mpf('-36.3609505192780257845662292664448'), 'GammaUDD[0][1][2]': mpf('-30.1270719072420555274502961949333'), 'GammaUDD[0][2][0]': mpf('12.2823492767347497337333129446469'), 'GammaUDD[0][2][1]': mpf('-30.1270719072420555274502961949333'), 'GammaUDD[0][2][2]': mpf('-22.2862334550824046529070435685497'), 'GammaUDD[1][0][0]': mpf('106.641099858555951710230668316931'), 'GammaUDD[1][0][1]': mpf('34.5648661443104393199809718733873'), 'GammaUDD[1][0][2]': mpf('-42.0625759749969397716067483012153'), 'GammaUDD[1][1][0]': mpf('34.5648661443104393199809718733873'), 'GammaUDD[1][1][1]': mpf('97.8702478474345566909160962391556'), 'GammaUDD[1][1][2]': mpf('83.7688065067695141694849527164939'), 'GammaUDD[1][2][0]': mpf('-42.0625759749969397716067483012153'), 'GammaUDD[1][2][1]': mpf('83.7688065067695141694849527164939'), 'GammaUDD[1][2][2]': mpf('63.37436120121238471219216463813'), 'GammaUDD[2][0][0]': mpf('-82.5821023178249589056509375957706'), 'GammaUDD[2][0][1]': mpf('-25.7920996188850360970982778501415'), 'GammaUDD[2][0][2]': mpf('53.7895042071558641126267308366467'), 'GammaUDD[2][1][0]': mpf('-25.7920996188850360970982778501415'), 'GammaUDD[2][1][1]': mpf('-115.685453925551790074619499114864'), 'GammaUDD[2][1][2]': mpf('-104.640881703073552107006493456468'), 'GammaUDD[2][2][0]': mpf('53.7895042071558641126267308366467'), 'GammaUDD[2][2][1]': mpf('-104.640881703073552107006493456468'), 'GammaUDD[2][2][2]': mpf('-81.2201272697658198766768439312511'), 'KDD[0][0]': mpf('2.52635313601760623671007177930783'), 'KDD[0][1]': mpf('2.82898873912114767238892405861643'), 'KDD[0][2]': mpf('0.381578303862588400274173947157554'), 'KDD[1][0]': mpf('2.82898873912114767238892405861643'), 'KDD[1][1]': mpf('0.775186746770488740436250426070358'), 'KDD[1][2]': mpf('0.547222495805151738810645787512339'), 'KDD[2][0]': mpf('0.381578303862588400274173947157554'), 'KDD[2][1]': mpf('0.547222495805151738810645787512339'), 'KDD[2][2]': mpf('0.23987159265969208367518412563801'), 'KDDdD[0][0][0]': mpf('-6.98418007348680855492680452748596'), 'KDDdD[0][0][1]': mpf('-4.90357255070945013899614827846758'), 'KDDdD[0][0][2]': mpf('-9.51091715630967778721956190135931'), 'KDDdD[0][1][0]': mpf('-4.23975060495945983382577294248919'), 'KDDdD[0][1][1]': mpf('-13.8610965865853556038884955897668'), 'KDDdD[0][1][2]': mpf('-10.9245220106877969893233753184212'), 'KDDdD[0][2][0]': mpf('0.673882280969017838122158707807608'), 'KDDdD[0][2][1]': mpf('-0.152233642969041739842056712724457'), 'KDDdD[0][2][2]': mpf('-0.869578101320992405108114411360838'), 'KDDdD[1][0][0]': mpf('-4.23975060495945983382577294248919'), 'KDDdD[1][0][1]': mpf('-13.8610965865853556038884955897668'), 'KDDdD[1][0][2]': mpf('-10.9245220106877969893233753184212'), 'KDDdD[1][1][0]': mpf('0.830817678793678957257737618165166'), 'KDDdD[1][1][1]': mpf('-3.44366499994735267064814741173414'), 'KDDdD[1][1][2]': mpf('-3.27432266931077674480141080049683'), 'KDDdD[1][2][0]': mpf('0.708601442458893682779601967212525'), 'KDDdD[1][2][1]': mpf('-1.5539589956391283812811555376677'), 'KDDdD[1][2][2]': mpf('-2.26158238807310704211409034124987'), 'KDDdD[2][0][0]': mpf('0.673882280969017838122158707807608'), 'KDDdD[2][0][1]': mpf('-0.152233642969041739842056712724457'), 'KDDdD[2][0][2]': mpf('-0.869578101320992405108114411360838'), 'KDDdD[2][1][0]': mpf('0.708601442458893682779601967212525'), 'KDDdD[2][1][1]': mpf('-1.5539589956391283812811555376677'), 'KDDdD[2][1][2]': mpf('-2.26158238807310704211409034124987'), 'KDDdD[2][2][0]': mpf('0.424502981369425679158130440942863'), 'KDDdD[2][2][1]': mpf('-0.116489737786019245029640728668932'), 'KDDdD[2][2][2]': mpf('-1.03790663578070485250054725692471')}
# Generated on: 2019-08-09
trusted_values_dict['BrillLindquist__BrillLindquist__ComputeADMGlobalsOnly=True__globals'] = {'alphaCart': mpf('0.343480750893445797669295337903236'), 'betaCartU[0]': mpf('0.0'), 'betaCartU[1]': mpf('0.0'), 'betaCartU[2]': mpf('0.0'), 'BCartU[0]': mpf('0.0'), 'BCartU[1]': mpf('0.0'), 'BCartU[2]': mpf('0.0'), 'gammaCartDD[0][0]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[0][1]': mpf('0.0'), 'gammaCartDD[0][2]': mpf('0.0'), 'gammaCartDD[1][0]': mpf('0.0'), 'gammaCartDD[1][1]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[1][2]': mpf('0.0'), 'gammaCartDD[2][0]': mpf('0.0'), 'gammaCartDD[2][1]': mpf('0.0'), 'gammaCartDD[2][2]': mpf('8.47608284216415538664572854966934'), 'KCartDD[0][0]': mpf('0.0'), 'KCartDD[0][1]': mpf('0.0'), 'KCartDD[0][2]': mpf('0.0'), 'KCartDD[1][0]': mpf('0.0'), 'KCartDD[1][1]': mpf('0.0'), 'KCartDD[1][2]': mpf('0.0'), 'KCartDD[2][0]': mpf('0.0'), 'KCartDD[2][1]': mpf('0.0'), 'KCartDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['Psi4__Psi4__specify_tetrad=False__globals'] = {'psi4_re_pt[0]': mpf('-11.4627408385133346012203475168749'), 'psi4_re_pt[1]': mpf('-35.6806714906029645727316589574499'), 'psi4_re_pt[2]': mpf('-3374.92496104893019517395960329346'), 'psi4_im_pt[0]': mpf('7.17626585537563385139665194926662'), 'psi4_im_pt[1]': mpf('103.321768247585408574682983348206'), 'psi4_im_pt[2]': mpf('-1785.88265198482805325630868693525')}
# Generated on: 2019-08-09
trusted_values_dict['Psi4_tetrads__Psi4_tetrads__globals'] = {'l4U[0]': mpf('0.707106781186547524400844362104785'), 'l4U[1]': mpf('0.238900425354320344832613106016182'), 'l4U[2]': mpf('0.0'), 'l4U[3]': mpf('-0.105712695337363311625493077156009'), 'n4U[0]': mpf('0.707106781186547524400844362104785'), 'n4U[1]': mpf('-0.238900425354320344832613106016182'), 'n4U[2]': mpf('0.0'), 'n4U[3]': mpf('0.105712695337363311625493077156009'), 'mre4U[0]': mpf('0.0'), 'mre4U[1]': mpc(real='0.0', imag='1.78934738466770903997371533478145'), 'mre4U[2]': mpc(real='0.0', imag='-5.09490051820905343049616931239143'), 'mre4U[3]': mpc(real='0.0', imag='6.3550480872706263113514069118537'), 'mim4U[0]': mpf('0.0'), 'mim4U[1]': mpf('0.0'), 'mim4U[2]': mpf('0.0'), 'mim4U[3]': mpf('1.06573776541005837815518683742699')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_RHSs__BSSN_RHSs__globals'] = {'cf_rhs': mpf('3.77382093789289698643326794257121'), 'trK_rhs': mpf('4897.15010872261926255101979442461'), 'lambda_rhsU[0]': mpf('-932925.380251880280514928387377418'), 'lambda_rhsU[1]': mpf('1067214.31502612160105983520112858'), 'lambda_rhsU[2]': mpf('-634521.485102957628967190217797872'), 'a_rhsDD[0][0]': mpf('140611.691921936258075248177043018'), 'a_rhsDD[0][1]': mpf('129610.009549628960496251001913799'), 'a_rhsDD[0][2]': mpf('12478.2048292408067063007598939149'), 'a_rhsDD[1][0]': mpf('129610.009549628960496251001913218'), 'a_rhsDD[1][1]': mpf('190869.710957991947993273525242575'), 'a_rhsDD[1][2]': mpf('127861.933393387548396211650851467'), 'a_rhsDD[2][0]': mpf('12478.2048292408067063007598938196'), 'a_rhsDD[2][1]': mpf('127861.933393387548396211650851428'), 'a_rhsDD[2][2]': mpf('200379.251226055957975675818299921'), 'h_rhsDD[0][0]': mpf('26.0204338410819173136453286914799'), 'h_rhsDD[0][1]': mpf('25.0575733174708969733897611044168'), 'h_rhsDD[0][2]': mpf('11.7768556092635889801475757434684'), 'h_rhsDD[1][0]': mpf('25.0575733174708969733897611044168'), 'h_rhsDD[1][1]': mpf('40.8921058052569946189333312205232'), 'h_rhsDD[1][2]': mpf('39.8230119150907530884376958638246'), 'h_rhsDD[2][0]': mpf('11.7768556092635889801475757434684'), 'h_rhsDD[2][1]': mpf('39.8230119150907530884376958638246'), 'h_rhsDD[2][2]': mpf('72.3354550710637207030251571354033')}
# Generated on: 2019-08-09
trusted_values_dict['ShiftedKerrSchild__ShiftedKerrSchild__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('0.764992222074068488402374263959388'), 'betaSphU[0]': mpf('0.414786900166179080948286822502086'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('1.70877924688282501671697638255698'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.83749814889950038479162273698221'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.428211355345928897234853289336263'), 'KSphDD[0][0]': mpf('-0.608416975836176206520434449342952'), 'KSphDD[0][1]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[0][2]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[1][0]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[1][1]': mpf('0.457311525223526834585984744200641'), 'KSphDD[1][2]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][0]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[2][1]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][2]': mpf('0.189536302720822065090385756720081')}
# Generated on: 2019-08-09
trusted_values_dict['StaticTrumpet__StaticTrumpet__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('0.47901157071628237535174768234896'), 'betaSphU[0]': mpf('0.249559485836202384477547321977731'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('4.35820838377928180149222863568597'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.456231514782622374769913065861258'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.19552817796951759898685705719116'), 'KSphDD[0][0]': mpf('-3.36157982955715545881983739302993'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.0'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.351901176504010315682080545229837'), 'KSphDD[1][2]': mpf('0.0'), 'KSphDD[2][0]': mpf('0.0'), 'KSphDD[2][1]': mpf('0.0'), 'KSphDD[2][2]': mpf('0.150815087598546495698285048292878')}
# Generated on: 2019-08-09
trusted_values_dict['T4UUmunu_vars__define_BSSN_T4UUmunu_rescaled_source_terms__globals'] = {'rho': mpf('0.318351756980246647366072920704028'), 'S': mpf('0.41672759971301631676254828562378'), 'sD[0]': mpf('0.139936033700502671095478035567794'), 'sD[1]': mpf('0.724042932336828037875875452300534'), 'sD[2]': mpf('0.634678894759461820207491200562799'), 'sDD[0][0]': mpf('0.975581143777359494073664336610818'), 'sDD[0][1]': mpf('0.319506043115684978950241657003062'), 'sDD[0][2]': mpf('0.843707417334013110021828651952092'), 'sDD[1][0]': mpf('0.319506043115684978950241657003062'), 'sDD[1][1]': mpf('0.535419446395211107159184393822215'), 'sDD[1][2]': mpf('0.697293983830049590721955610206351'), 'sDD[2][0]': mpf('0.843707417334013110021828651952092'), 'sDD[2][1]': mpf('0.697293983830049590721955610206351'), 'sDD[2][2]': mpf('0.0883241044811280406889864025288261')}
# Generated on: 2019-08-09
trusted_values_dict['UIUCBlackHole__UIUCBlackHole__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('1.0'), 'betaSphU[0]': mpf('0.0'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('5.88800397535091508054696345959799'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.49579570354855419371061261468577'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.239531368774223457427342816780148'), 'KSphDD[0][0]': mpf('0.0'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.516384223238703786717270771044344'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.0'), 'KSphDD[1][2]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][0]': mpf('0.516384223238703786717270771044344'), 'KSphDD[2][1]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_constraints__BSSN_constraints__globals'] = {'H': mpf('-743404.980107555200155796229460404'), 'MU[0]': mpf('180487.187683651511140228998931702'), 'MU[1]': mpf('-212737.320962133501867845763983593'), 'MU[2]': mpf('117663.393460122555345796445578668')}
# Generated on: 2019-08-09
trusted_values_dict['gauge_RHSs__BSSN_gauge_RHSs__globals'] = {'alpha_rhs': mpf('4.55215595841202287904255791161233'), 'bet_rhsU[0]': mpf('-699763.514938021371061944114376614'), 'bet_rhsU[1]': mpf('800493.882262268190058064264269136'), 'bet_rhsU[2]': mpf('-475937.852585205029810577745084266'), 'vet_rhsU[0]': mpf('-202.50075396040237046126314033238'), 'vet_rhsU[1]': mpf('243.191035932372271446347823729945'), 'vet_rhsU[2]': mpf('-122.639420805832850593976051533138')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__declare_BSSN_gridfunctions_if_not_declared_already__globals'] = {'hDD[0][0]': mpf('0.00450974412619198350427041077637114'), 'hDD[0][1]': mpf('0.923519781999295741314881524886005'), 'hDD[0][2]': mpf('0.120267555611392129577552623231895'), 'hDD[1][0]': mpf('0.923519781999295741314881524886005'), 'hDD[1][1]': mpf('0.290125263019792467567015137319686'), 'hDD[1][2]': mpf('0.925654825647319157155834545847028'), 'hDD[2][0]': mpf('0.120267555611392129577552623231895'), 'hDD[2][1]': mpf('0.925654825647319157155834545847028'), 'hDD[2][2]': mpf('0.477887576370744082687735954095842'), 'aDD[0][0]': mpf('0.142813553146530880511022587597836'), 'aDD[0][1]': mpf('0.647457955584693545247887414006982'), 'aDD[0][2]': mpf('0.21834217753528206706903347367188'), 'aDD[1][0]': mpf('0.647457955584693545247887414006982'), 'aDD[1][1]': mpf('0.333850909184938648976981312443968'), 'aDD[1][2]': mpf('0.687554902414082569350739504443482'), 'aDD[2][0]': mpf('0.21834217753528206706903347367188'), 'aDD[2][1]': mpf('0.687554902414082569350739504443482'), 'aDD[2][2]': mpf('0.593368548750377144962442343967268'), 'lambdaU[0]': mpf('0.562783861711218968970626974623883'), 'lambdaU[1]': mpf('0.664424004972256732060031936271116'), 'lambdaU[2]': mpf('0.26459724357734237809580690736766'), 'vetU[0]': mpf('0.696603667756184763426574590994278'), 'vetU[1]': mpf('0.602357411405810894144963185681263'), 'vetU[2]': mpf('0.850413289206002676046125543507515'), 'betU[0]': mpf('0.0391630274878592699039359104062896'), 'betU[1]': mpf('0.0690677340046900178904820677416865'), 'betU[2]': mpf('0.210433354840527941931327404745389'), 'trK': mpf('0.430179315304250131646313093369827'), 'cf': mpf('0.336963542644043156926159099384677'), 'alpha': mpf('0.574476527200906694758941739564762')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__BSSN_basic_tensors__globals'] = {'gammabarDD[0][0]': mpf('1.00450974412619198350427041077637'), 'gammabarDD[0][1]': mpf('0.380375959488448974007020456949004'), 'gammabarDD[0][2]': mpf('0.0221179741925291571131550303027706'), 'gammabarDD[1][0]': mpf('0.380375959488448974007020456949004'), 'gammabarDD[1][1]': mpf('0.218859597510979935870263689587696'), 'gammabarDD[1][2]': mpf('0.0701152990412707885651168831120483'), 'gammabarDD[2][0]': mpf('0.0221179741925291571131550303027706'), 'gammabarDD[2][1]': mpf('0.0701152990412707885651168831120483'), 'gammabarDD[2][2]': mpf('0.0499844737257731260766363781664902'), 'AbarDD[0][0]': mpf('0.142813553146530880511022587597836'), 'AbarDD[0][1]': mpf('0.266672621295452871655360256826897'), 'AbarDD[0][2]': mpf('0.0401545256600238301157730952990965'), 'AbarDD[1][0]': mpf('0.266672621295452871655360256826897'), 'AbarDD[1][1]': mpf('0.056635179317288849494133380201829'), 'AbarDD[1][2]': mpf('0.0520800154164839604018649639497029'), 'AbarDD[2][0]': mpf('0.0401545256600238301157730952990965'), 'AbarDD[2][1]': mpf('0.0520800154164839604018649639497029'), 'AbarDD[2][2]': mpf('0.0200686541445511291805537698984873'), 'LambdabarU[0]': mpf('0.562783861711218968970626974623883'), 'LambdabarU[1]': mpf('1.61316375791018207963945540610151'), 'LambdabarU[2]': mpf('1.43876032359725990511334321483131'), 'betaU[0]': mpf('0.696603667756184763426574590994278'), 'betaU[1]': mpf('1.46247146117037299492483621025343'), 'betaU[2]': mpf('4.6241634365771266519514488292252'), 'BU[0]': mpf('0.0391630274878592699039359104062896'), 'BU[1]': mpf('0.167690457454195833796083765751675'), 'BU[2]': mpf('1.14424155600667335640923075733714')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__gammabar__inverse_and_derivs__globals'] = {'gammabarUU[0][0]': mpf('-55.3913683921929939892608290454462'), 'gammabarUU[0][1]': mpf('160.581139770070431806627488067463'), 'gammabarUU[0][2]': mpf('-200.743331495864219543840762334583'), 'gammabarUU[1][0]': mpf('160.581139770070431806627488067463'), 'gammabarUU[1][1]': mpf('-457.230909570455507522618494885285'), 'gammabarUU[1][2]': mpf('570.320147944349793491547665601545'), 'gammabarUU[2][0]': mpf('-200.743331495864219543840762334583'), 'gammabarUU[2][1]': mpf('570.320147944349793491547665601545'), 'gammabarUU[2][2]': mpf('-691.177266096096440977423920582226'), 'gammabarDD_dD[0][0][0]': mpf('0.113376527866793974652637189137749'), 'gammabarDD_dD[0][0][1]': mpf('0.597031746016325581827288715430768'), 'gammabarDD_dD[0][0][2]': mpf('0.0326900992587932925914628867758438'), 'gammabarDD_dD[0][1][0]': mpf('1.29509849818179299105146594183329'), 'gammabarDD_dD[0][1][1]': mpf('0.29966757015887334440305520963308'), 'gammabarDD_dD[0][1][2]': mpf('0.408456984468846210692212242298237'), 'gammabarDD_dD[0][2][0]': mpf('0.0757914276030065310563752344719272'), 'gammabarDD_dD[0][2][1]': mpf('0.211213637778013737361842492406609'), 'gammabarDD_dD[0][2][2]': mpf('0.043576970886389692140854474231034'), 'gammabarDD_dD[1][0][0]': mpf('1.29509849818179299105146594183329'), 'gammabarDD_dD[1][0][1]': mpf('0.29966757015887334440305520963308'), 'gammabarDD_dD[1][0][2]': mpf('0.408456984468846210692212242298237'), 'gammabarDD_dD[1][1][0]': mpf('1.11081293166050470517099765479562'), 'gammabarDD_dD[1][1][1]': mpf('0.162456880553023991817285210787008'), 'gammabarDD_dD[1][1][2]': mpf('0.158500072577490769771706912461303'), 'gammabarDD_dD[1][2][0]': mpf('0.359283025128967082047900286877992'), 'gammabarDD_dD[1][2][1]': mpf('0.199267454573993880968860334987007'), 'gammabarDD_dD[1][2][2]': mpf('0.0222668035023729125133168779194776'), 'gammabarDD_dD[2][0][0]': mpf('0.0757914276030065310563752344719272'), 'gammabarDD_dD[2][0][1]': mpf('0.211213637778013737361842492406609'), 'gammabarDD_dD[2][0][2]': mpf('0.043576970886389692140854474231034'), 'gammabarDD_dD[2][1][0]': mpf('0.359283025128967082047900286877992'), 'gammabarDD_dD[2][1][1]': mpf('0.199267454573993880968860334987007'), 'gammabarDD_dD[2][1][2]': mpf('0.0222668035023729125133168779194776'), 'gammabarDD_dD[2][2][0]': mpf('0.249364796420989096924555910811589'), 'gammabarDD_dD[2][2][1]': mpf('0.229090281001197346827970614026702'), 'gammabarDD_dD[2][2][2]': mpf('0.0293354706024628609686096695909266'), 'gammabarDD_dupD[0][0][0]': mpf('0.37728180260573840332227746330318'), 'gammabarDD_dupD[0][0][1]': mpf('0.670267766719132906949596417689463'), 'gammabarDD_dupD[0][0][2]': mpf('0.781414536731840470018539690499892'), 'gammabarDD_dupD[0][1][0]': mpf('1.27559379975272638197786370234481'), 'gammabarDD_dupD[0][1][1]': mpf('0.270519930552051576420169894439394'), 'gammabarDD_dupD[0][1][2]': mpf('0.126974122612752350149982986405959'), 'gammabarDD_dupD[0][2][0]': mpf('0.160014994496667253968983142186692'), 'gammabarDD_dupD[0][2][1]': mpf('0.159417776511127717981289959174267'), 'gammabarDD_dupD[0][2][2]': mpf('0.0914222006910319288079363366980038'), 'gammabarDD_dupD[1][0][0]': mpf('1.27559379975272638197786370234481'), 'gammabarDD_dupD[1][0][1]': mpf('0.270519930552051576420169894439394'), 'gammabarDD_dupD[1][0][2]': mpf('0.126974122612752350149982986405959'), 'gammabarDD_dupD[1][1][0]': mpf('1.11565554836934532567070034274662'), 'gammabarDD_dupD[1][1][1]': mpf('0.0225054491023032484277595044174296'), 'gammabarDD_dupD[1][1][2]': mpf('0.148236212186685765996028318648501'), 'gammabarDD_dupD[1][2][0]': mpf('0.38244111267429014279462120452751'), 'gammabarDD_dupD[1][2][1]': mpf('0.19605291006032265256196166097098'), 'gammabarDD_dupD[1][2][2]': mpf('0.000359393084847625499631216495471881'), 'gammabarDD_dupD[2][0][0]': mpf('0.160014994496667253968983142186692'), 'gammabarDD_dupD[2][0][1]': mpf('0.159417776511127717981289959174267'), 'gammabarDD_dupD[2][0][2]': mpf('0.0914222006910319288079363366980038'), 'gammabarDD_dupD[2][1][0]': mpf('0.38244111267429014279462120452751'), 'gammabarDD_dupD[2][1][1]': mpf('0.19605291006032265256196166097098'), 'gammabarDD_dupD[2][1][2]': mpf('0.000359393084847625499631216495471881'), 'gammabarDD_dupD[2][2][0]': mpf('0.255124908396158921845552323395099'), 'gammabarDD_dupD[2][2][1]': mpf('0.210716113114337146067597109416175'), 'gammabarDD_dupD[2][2][2]': mpf('0.01339819778908501331879521055357'), 'gammabarDD_dDD[0][0][0][0]': mpf('0.185845801018537204463143552857218'), 'gammabarDD_dDD[0][0][0][1]': mpf('0.41265964722127601582712941308273'), 'gammabarDD_dDD[0][0][0][2]': mpf('0.37985477211695328758622736131656'), 'gammabarDD_dDD[0][0][1][0]': mpf('0.41265964722127601582712941308273'), 'gammabarDD_dDD[0][0][1][1]': mpf('0.689211123606880748937442149326671'), 'gammabarDD_dDD[0][0][1][2]': mpf('0.0048337295380591616122956111212261'), 'gammabarDD_dDD[0][0][2][0]': mpf('0.37985477211695328758622736131656'), 'gammabarDD_dDD[0][0][2][1]': mpf('0.0048337295380591616122956111212261'), 'gammabarDD_dDD[0][0][2][2]': mpf('0.538905401817390661278750485507771'), 'gammabarDD_dDD[0][1][0][0]': mpf('2.17695434404486827829573669497839'), 'gammabarDD_dDD[0][1][0][1]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[0][1][0][2]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[0][1][1][0]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[0][1][1][1]': mpf('0.0572033349261599444767919389310414'), 'gammabarDD_dDD[0][1][1][2]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[0][1][2][0]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[0][1][2][1]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[0][1][2][2]': mpf('0.336172859503064373361339781168181'), 'gammabarDD_dDD[0][2][0][0]': mpf('0.149593239036693143279810874442325'), 'gammabarDD_dDD[0][2][0][1]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[0][2][0][2]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[0][2][1][0]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[0][2][1][1]': mpf('0.659480314934221443474116011063672'), 'gammabarDD_dDD[0][2][1][2]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[0][2][2][0]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[0][2][2][1]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[0][2][2][2]': mpf('0.0131768891907001501822079393602128'), 'gammabarDD_dDD[1][0][0][0]': mpf('2.17695434404486827829573669497839'), 'gammabarDD_dDD[1][0][0][1]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[1][0][0][2]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[1][0][1][0]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[1][0][1][1]': mpf('0.0572033349261599444767919389310414'), 'gammabarDD_dDD[1][0][1][2]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[1][0][2][0]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[1][0][2][1]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[1][0][2][2]': mpf('0.336172859503064373361339781168181'), 'gammabarDD_dDD[1][1][0][0]': mpf('3.2125897978005476278879035565663'), 'gammabarDD_dDD[1][1][0][1]': mpf('0.89598315374067949012654219762452'), 'gammabarDD_dDD[1][1][0][2]': mpf('0.820646830703092166810003596680962'), 'gammabarDD_dDD[1][1][1][0]': mpf('0.89598315374067949012654219762452'), 'gammabarDD_dDD[1][1][1][1]': mpf('0.168996792900712596062122767009282'), 'gammabarDD_dDD[1][1][1][2]': mpf('0.0473971971696919594315542625878797'), 'gammabarDD_dDD[1][1][2][0]': mpf('0.820646830703092166810003596680962'), 'gammabarDD_dDD[1][1][2][1]': mpf('0.0473971971696919594315542625878797'), 'gammabarDD_dDD[1][1][2][2]': mpf('0.12968746168617386620955233939796'), 'gammabarDD_dDD[1][2][0][0]': mpf('1.04180796862731772248736459987955'), 'gammabarDD_dDD[1][2][0][1]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[1][2][0][2]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[1][2][1][0]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[1][2][1][1]': mpf('0.205354158028681326149566579067253'), 'gammabarDD_dDD[1][2][1][2]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[1][2][2][0]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[1][2][2][1]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[1][2][2][2]': mpf('0.0737488709653608336887200438662802'), 'gammabarDD_dDD[2][0][0][0]': mpf('0.149593239036693143279810874442325'), 'gammabarDD_dDD[2][0][0][1]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[2][0][0][2]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[2][0][1][0]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[2][0][1][1]': mpf('0.659480314934221443474116011063672'), 'gammabarDD_dDD[2][0][1][2]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[2][0][2][0]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[2][0][2][1]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[2][0][2][2]': mpf('0.0131768891907001501822079393602128'), 'gammabarDD_dDD[2][1][0][0]': mpf('1.04180796862731772248736459987955'), 'gammabarDD_dDD[2][1][0][1]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[2][1][0][2]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[2][1][1][0]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[2][1][1][1]': mpf('0.205354158028681326149566579067253'), 'gammabarDD_dDD[2][1][1][2]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[2][1][2][0]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[2][1][2][1]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[2][1][2][2]': mpf('0.0737488709653608336887200438662802'), 'gammabarDD_dDD[2][2][0][0]': mpf('0.66508879899869765324495129089009'), 'gammabarDD_dDD[2][2][0][1]': mpf('1.15812919014671874295450095368265'), 'gammabarDD_dDD[2][2][0][2]': mpf('0.165896787206516112586624167221097'), 'gammabarDD_dDD[2][2][1][0]': mpf('1.15812919014671874295450095368285'), 'gammabarDD_dDD[2][2][1][1]': mpf('0.533174868302490566724214906488119'), 'gammabarDD_dDD[2][2][1][2]': mpf('0.131976505361826978863691234258533'), 'gammabarDD_dDD[2][2][2][0]': mpf('0.165896787206516112586624167221097'), 'gammabarDD_dDD[2][2][2][1]': mpf('0.131976505361826978863691234258533'), 'gammabarDD_dDD[2][2][2][2]': mpf('0.010262022536939967495395958633956'), 'GammabarUDD[0][0][0]': mpf('144.958869354596952650223295197696'), 'GammabarUDD[0][0][1]': mpf('56.3884081838815055298106227619069'), 'GammabarUDD[0][0][2]': mpf('18.7492848883834673075437947835502'), 'GammabarUDD[0][1][0]': mpf('56.3884081838815055298106227619069'), 'GammabarUDD[0][1][1]': mpf('3.11678651957992149533502697995063'), 'GammabarUDD[0][1][2]': mpf('-17.4797246150434126590734188209953'), 'GammabarUDD[0][2][0]': mpf('18.7492848883834673075437947835502'), 'GammabarUDD[0][2][1]': mpf('-17.4797246150434126590734188209953'), 'GammabarUDD[0][2][2]': mpf('-13.2700699776147156200210831819353'), 'GammabarUDD[1][0][0]': mpf('-412.661847055574875860384479574194'), 'GammabarUDD[1][0][1]': mpf('-159.805737735924117067707092684796'), 'GammabarUDD[1][0][2]': mpf('-53.4969391010037688000184007256669'), 'GammabarUDD[1][1][0]': mpf('-159.805737735924117067707092684796'), 'GammabarUDD[1][1][1]': mpf('-9.75864522872634062530478027925608'), 'GammabarUDD[1][1][2]': mpf('49.998503864403681839928229922025'), 'GammabarUDD[1][2][0]': mpf('-53.4969391010037688000184007256669'), 'GammabarUDD[1][2][1]': mpf('49.998503864403681839928229922025'), 'GammabarUDD[1][2][2]': mpf('37.5338109651112817021709810969423'), 'GammabarUDD[2][0][0]': mpf('515.903374261971431760645349581284'), 'GammabarUDD[2][0][1]': mpf('200.835355938522818161227492367693'), 'GammabarUDD[2][0][2]': mpf('69.2403025140953793071085058377914'), 'GammabarUDD[2][1][0]': mpf('200.835355938522818161227492367693'), 'GammabarUDD[2][1][1]': mpf('14.7107829316252639239815376245426'), 'GammabarUDD[2][1][2]': mpf('-60.1086414978290381532119255024356'), 'GammabarUDD[2][2][0]': mpf('69.2403025140953793071085058377914'), 'GammabarUDD[2][2][1]': mpf('-60.1086414978290381532119255024356'), 'GammabarUDD[2][2][2]': mpf('-46.4848263107765220270562375920812')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__detgammabar_and_derivs__globals'] = {'detgammabar': mpf('0.00573756266880972544377979111558305'), 'detgammabar_dD[0]': mpf('0.0557212146869903791885764536246544'), 'detgammabar_dD[1]': mpf('0.0229955254712656127315158404877634'), 'detgammabar_dD[2]': mpf('0.0'), 'detgammabar_dDD[0][0]': mpf('0.405858804349815958396163363914466'), 'detgammabar_dDD[0][1]': mpf('0.223324551832801915665195788045795'), 'detgammabar_dDD[0][2]': mpf('0.0'), 'detgammabar_dDD[1][0]': mpf('0.223324551832801915665195788045795'), 'detgammabar_dDD[1][1]': mpf('0.0346066538274459059495911839041824'), 'detgammabar_dDD[1][2]': mpf('0.0'), 'detgammabar_dDD[2][0]': mpf('0.0'), 'detgammabar_dDD[2][1]': mpf('0.0'), 'detgammabar_dDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__AbarUU_AbarUD_trAbar_AbarDD_dD__globals'] = {'AbarUU[0][0]': mpf('-4501.35489903001232813737257379218'), 'AbarUU[0][1]': mpf('12891.1260926920183373060745066027'), 'AbarUU[0][2]': mpf('-16048.8656589832488120714141642617'), 'AbarUU[1][0]': mpf('12891.1260926920183373060745066027'), 'AbarUU[1][1]': mpf('-36915.8909445128919893699783581937'), 'AbarUU[1][2]': mpf('45960.7552497439495397460051934269'), 'AbarUU[2][0]': mpf('-16048.8656589832488120714141642617'), 'AbarUU[2][1]': mpf('45960.7552497439495397460051934269'), 'AbarUU[2][2]': mpf('-57214.0298282929626166132996415947'), 'AbarUD[0][0]': mpf('26.8512020837293036423215049007785'), 'AbarUD[0][1]': mpf('-16.1315355594965963827373776014512'), 'AbarUD[0][2]': mpf('2.11020561975821049709560254567513'), 'AbarUD[1][0]': mpf('-76.096867038514101536679331070774'), 'AbarUD[1][1]': mpf('46.6295210174394985755149432015637'), 'AbarUD[1][2]': mpf('-5.91897552114475146319310921096151'), 'AbarUD[2][0]': mpf('95.6660051214531525255013492129713'), 'AbarUD[2][1]': mpf('-57.2290892443011388753856943624776'), 'AbarUD[2][2]': mpf('7.77053133578485609602163485965202'), 'trAbar': mpf('81.251254436953658313858082961098'), 'AbarDD_dD[0][0][0]': mpf('0.33721198437911092948837676885887'), 'AbarDD_dD[0][0][1]': mpf('0.679909473427153132618627751071472'), 'AbarDD_dD[0][0][2]': mpf('0.327242304499680503049319213459967'), 'AbarDD_dD[0][1][0]': mpf('0.733454489269375682720410108340373'), 'AbarDD_dD[0][1][1]': mpf('0.0564054522687123879076101957745309'), 'AbarDD_dD[0][1][2]': mpf('0.288952399746149364416592105125356'), 'AbarDD_dD[0][2][0]': mpf('0.259134566883295043505905621463171'), 'AbarDD_dD[0][2][1]': mpf('0.18566392834089806327031028199579'), 'AbarDD_dD[0][2][2]': mpf('0.10947288045690455436180777612326'), 'AbarDD_dD[1][0][0]': mpf('0.733454489269375682720410108340373'), 'AbarDD_dD[1][0][1]': mpf('0.0564054522687123879076101957745309'), 'AbarDD_dD[1][0][2]': mpf('0.288952399746149364416592105125356'), 'AbarDD_dD[1][1][0]': mpf('0.300852467812027766391019475297803'), 'AbarDD_dD[1][1][1]': mpf('0.0145399155791957464680340380309724'), 'AbarDD_dD[1][1][2]': mpf('0.0395378448600819201304333750185868'), 'AbarDD_dD[1][2][0]': mpf('0.300398363998209101604122555846344'), 'AbarDD_dD[1][2][1]': mpf('0.119537195366846939854213812053404'), 'AbarDD_dD[1][2][2]': mpf('0.04721114477346884448153824722088'), 'AbarDD_dD[2][0][0]': mpf('0.259134566883295043505905621463171'), 'AbarDD_dD[2][0][1]': mpf('0.18566392834089806327031028199579'), 'AbarDD_dD[2][0][2]': mpf('0.10947288045690455436180777612326'), 'AbarDD_dD[2][1][0]': mpf('0.300398363998209101604122555846344'), 'AbarDD_dD[2][1][1]': mpf('0.119537195366846939854213812053404'), 'AbarDD_dD[2][1][2]': mpf('0.04721114477346884448153824722088'), 'AbarDD_dD[2][2][0]': mpf('0.128416701185305713723338911303584'), 'AbarDD_dD[2][2][1]': mpf('0.091211183233673576891878621321523'), 'AbarDD_dD[2][2][2]': mpf('0.0124213262600154053715160803941519'), 'AbarDD_dupD[0][0][0]': mpf('0.769930872140056510311012516467599'), 'AbarDD_dupD[0][0][1]': mpf('0.406856967532203017690051183308242'), 'AbarDD_dupD[0][0][2]': mpf('0.034573833670986209298803260026034'), 'AbarDD_dupD[0][1][0]': mpf('0.799078538941273777740809109133389'), 'AbarDD_dupD[0][1][1]': mpf('0.111116568306179455959424903906438'), 'AbarDD_dupD[0][1][2]': mpf('0.377511909918297386621098531539175'), 'AbarDD_dupD[0][2][0]': mpf('0.214500608396807435491964973223518'), 'AbarDD_dupD[0][2][1]': mpf('0.115344958017771695940477015182743'), 'AbarDD_dupD[0][2][2]': mpf('0.122244734709826544219416183615818'), 'AbarDD_dupD[1][0][0]': mpf('0.799078538941273777740809109133389'), 'AbarDD_dupD[1][0][1]': mpf('0.111116568306179455959424903906438'), 'AbarDD_dupD[1][0][2]': mpf('0.377511909918297386621098531539175'), 'AbarDD_dupD[1][1][0]': mpf('0.435501861817190390787863358691289'), 'AbarDD_dupD[1][1][1]': mpf('0.116623980226106608051172387504808'), 'AbarDD_dupD[1][1][2]': mpf('0.0029262421496022085618519737385143'), 'AbarDD_dupD[1][2][0]': mpf('0.319628992953124375855747562860006'), 'AbarDD_dupD[1][2][1]': mpf('0.154649167361595245816006653565866'), 'AbarDD_dupD[1][2][2]': mpf('0.0691883280846439197601862955001518'), 'AbarDD_dupD[2][0][0]': mpf('0.214500608396807435491964973223518'), 'AbarDD_dupD[2][0][1]': mpf('0.115344958017771695940477015182743'), 'AbarDD_dupD[2][0][2]': mpf('0.122244734709826544219416183615818'), 'AbarDD_dupD[2][1][0]': mpf('0.319628992953124375855747562860006'), 'AbarDD_dupD[2][1][1]': mpf('0.154649167361595245816006653565866'), 'AbarDD_dupD[2][1][2]': mpf('0.0691883280846439197601862955001518'), 'AbarDD_dupD[2][2][0]': mpf('0.102626201407641450303174373971461'), 'AbarDD_dupD[2][2][1]': mpf('0.102313858934520263961250056659667'), 'AbarDD_dupD[2][2][2]': mpf('0.00853496939586718772700035856415055')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU__globals'] = {'RbarDD[0][0]': mpf('-41245.2193607369706612392062712087'), 'RbarDD[0][1]': mpf('-13279.8476666719541384124392490902'), 'RbarDD[0][2]': mpf('-13136.4852727140129778266912775021'), 'RbarDD[1][0]': mpf('-13279.8476666719541384124392490142'), 'RbarDD[1][1]': mpf('17572.3339460500557245822009043625'), 'RbarDD[1][2]': mpf('-4983.05391806650925004378740779'), 'RbarDD[2][0]': mpf('-13136.485272714012977826691277452'), 'RbarDD[2][1]': mpf('-4983.05391806650925004378740779404'), 'RbarDD[2][2]': mpf('-5521.17554758589382532917232982535'), 'DGammaUDD[0][0][0]': mpf('144.958869354596952650223295197696'), 'DGammaUDD[0][0][1]': mpf('56.3884081838815055298106227619069'), 'DGammaUDD[0][0][2]': mpf('18.7492848883834673075437947835502'), 'DGammaUDD[0][1][0]': mpf('56.3884081838815055298106227619069'), 'DGammaUDD[0][1][1]': mpf('3.52866287231487465035648484008052'), 'DGammaUDD[0][1][2]': mpf('-17.4797246150434126590734188209953'), 'DGammaUDD[0][2][0]': mpf('18.7492848883834673075437947835502'), 'DGammaUDD[0][2][1]': mpf('-17.4797246150434126590734188209953'), 'DGammaUDD[0][2][2]': mpf('-13.1879541513368329697779753752397'), 'DGammaUDD[1][0][0]': mpf('-412.661847055574875860384479574194'), 'DGammaUDD[1][0][1]': mpf('-162.233650854411671297209410338331'), 'DGammaUDD[1][0][2]': mpf('-53.4969391010037688000184007256669'), 'DGammaUDD[1][1][0]': mpf('-162.233650854411671297209410338331'), 'DGammaUDD[1][1][1]': mpf('-9.75864522872634062530478027925608'), 'DGammaUDD[1][1][2]': mpf('49.998503864403681839928229922025'), 'DGammaUDD[1][2][0]': mpf('-53.4969391010037688000184007256669'), 'DGammaUDD[1][2][1]': mpf('49.998503864403681839928229922025'), 'DGammaUDD[1][2][2]': mpf('37.9333377581165059513560056676581'), 'DGammaUDD[2][0][0]': mpf('515.903374261971431760645349581284'), 'DGammaUDD[2][0][1]': mpf('200.835355938522818161227492367693'), 'DGammaUDD[2][0][2]': mpf('66.8123893956078250776061881842562'), 'DGammaUDD[2][1][0]': mpf('200.835355938522818161227492367693'), 'DGammaUDD[2][1][1]': mpf('14.7107829316252639239815376245426'), 'DGammaUDD[2][1][2]': mpf('-62.1125869707275593077009803716383'), 'DGammaUDD[2][2][0]': mpf('66.8123893956078250776061881842562'), 'DGammaUDD[2][2][1]': mpf('-62.1125869707275593077009803716383'), 'DGammaUDD[2][2][2]': mpf('-46.4848263107765220270562375920812'), 'gammabarDD_dHatD[0][0][0]': mpf('0.113376527866793974652637189137749'), 'gammabarDD_dHatD[0][0][1]': mpf('-1.25000781798226590080247433434124'), 'gammabarDD_dHatD[0][0][2]': mpf('-0.0747109401340281274447427294117478'), 'gammabarDD_dHatD[0][1][0]': mpf('0.371578716182497249736584416947285'), 'gammabarDD_dHatD[0][1][1]': mpf('0.182029291952476074147915289630397'), 'gammabarDD_dHatD[0][1][2]': mpf('0.193899915867061900813055159582757'), 'gammabarDD_dHatD[0][2][0]': mpf('0.0220909079065958210382724263781314'), 'gammabarDD_dHatD[0][2][1]': mpf('-0.00334343082377057251731459030886539'), 'gammabarDD_dHatD[0][2][2]': mpf('0.156675546280700493851442555008333'), 'gammabarDD_dHatD[1][0][0]': mpf('0.371578716182497249736584416947285'), 'gammabarDD_dHatD[1][0][1]': mpf('0.182029291952476074147915289630397'), 'gammabarDD_dHatD[1][0][2]': mpf('0.193899915867061900813055159582757'), 'gammabarDD_dHatD[1][1][0]': mpf('0.0480687558528761896352673763644066'), 'gammabarDD_dHatD[1][1][1]': mpf('0.475792606277345313962293654458942'), 'gammabarDD_dHatD[1][1][2]': mpf('-0.122514399611870464853979699604412'), 'gammabarDD_dHatD[1][2][0]': mpf('0.0188153164310087189522141894729995'), 'gammabarDD_dHatD[1][2][1]': mpf('0.0678700890196179934569006598920808'), 'gammabarDD_dHatD[1][2][2]': mpf('0.0407758029860071575845246674537359'), 'gammabarDD_dHatD[2][0][0]': mpf('0.0220909079065958210382724263781314'), 'gammabarDD_dHatD[2][0][1]': mpf('-0.00334343082377057251731459030886539'), 'gammabarDD_dHatD[2][0][2]': mpf('0.156675546280700493851442555008333'), 'gammabarDD_dHatD[2][1][0]': mpf('0.0188153164310087189522141894729995'), 'gammabarDD_dHatD[2][1][1]': mpf('0.0678700890196179934569006598920808'), 'gammabarDD_dHatD[2][1][2]': mpf('0.0407758029860071575845246674537359'), 'gammabarDD_dHatD[2][2][0]': mpf('0.00664887746198699878899292414052604'), 'gammabarDD_dHatD[2][2][1]': mpf('0.0287579613252410813496304259029699'), 'gammabarDD_dHatD[2][2][2]': mpf('0.0889938231884100782604806662221182'), 'DGammaU[0]': mpf('-9883.50614138972832359869160635136'), 'DGammaU[1]': mpf('27506.4846155065370543113398270077'), 'DGammaU[2]': mpf('-36345.2254745846641825231815918191')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__betaU_derivs__globals'] = {'betaU_dD[0][0]': mpf('0.192243539484738046319023396790726'), 'betaU_dD[0][1]': mpf('0.754980728051157679026061941840453'), 'betaU_dD[0][2]': mpf('0.586123078895915305963626451557502'), 'betaU_dD[1][0]': mpf('-3.18830793589382952185971488180311'), 'betaU_dD[1][1]': mpf('0.922655491152517150129828380218527'), 'betaU_dD[1][2]': mpf('2.402141784188005363245692498957'), 'betaU_dD[2][0]': mpf('-8.9043744551685352332500234492703'), 'betaU_dD[2][1]': mpf('-4.62598533129686961830418682100716'), 'betaU_dD[2][2]': mpf('3.76687640853700760943325487483147'), 'betaU_dupD[0][0]': mpf('0.659472189904469763277461424877401'), 'betaU_dupD[0][1]': mpf('0.948628367838908470588421550928615'), 'betaU_dupD[0][2]': mpf('0.669133180747105038577160485147033'), 'betaU_dupD[1][0]': mpf('-1.60394693865713402126230258890783'), 'betaU_dupD[1][1]': mpf('1.41044106421919101151961432862351'), 'betaU_dupD[1][2]': mpf('0.406302118865459317620854482607423'), 'betaU_dupD[2][0]': mpf('-6.34070058470575134851313539411892'), 'betaU_dupD[2][1]': mpf('-8.20689620375936786515281493126901'), 'betaU_dupD[2][2]': mpf('3.95742743877129970876759452390656'), 'betaU_dDD[0][0][0]': mpf('0.693440743164982609236801636143355'), 'betaU_dDD[0][0][1]': mpf('0.474644061200962252833335242030444'), 'betaU_dDD[0][0][2]': mpf('0.248757440072191138291657352965558'), 'betaU_dDD[0][1][0]': mpf('0.474644061200962252833335242030444'), 'betaU_dDD[0][1][1]': mpf('0.0806331535826826373281051019148435'), 'betaU_dDD[0][1][2]': mpf('0.906200949230643648668603873375105'), 'betaU_dDD[0][2][0]': mpf('0.248757440072191138291657352965558'), 'betaU_dDD[0][2][1]': mpf('0.906200949230643648668603873375105'), 'betaU_dDD[0][2][2]': mpf('0.256446105879290064955000616464531'), 'betaU_dDD[1][0][0]': mpf('17.6480807195419839436961496336133'), 'betaU_dDD[1][0][1]': mpf('-0.521955208304153160154395471306198'), 'betaU_dDD[1][0][2]': mpf('-3.59469879181060009166143907294526'), 'betaU_dDD[1][1][0]': mpf('-0.521955208304153160154395471306198'), 'betaU_dDD[1][1][1]': mpf('0.390333580442849126669804861962297'), 'betaU_dDD[1][1][2]': mpf('1.03879140080831086038810101219714'), 'betaU_dDD[1][2][0]': mpf('-3.59469879181060009166143907294526'), 'betaU_dDD[1][2][1]': mpf('1.03879140080831086038810101219714'), 'betaU_dDD[1][2][2]': mpf('0.429021139775372473874225378432791'), 'betaU_dDD[2][0][0]': mpf('47.7279356778735564316328362884232'), 'betaU_dDD[2][0][1]': mpf('11.3305777369963646737013674981541'), 'betaU_dDD[2][0][2]': mpf('-9.09922406127764796777495296898437'), 'betaU_dDD[2][1][0]': mpf('11.3305777369963646737013674981541'), 'betaU_dDD[2][1][1]': mpf('24.3353794441474582057173283509946'), 'betaU_dDD[2][1][2]': mpf('-3.95193536311042053355897439286505'), 'betaU_dDD[2][2][0]': mpf('-9.09922406127764796777495296898437'), 'betaU_dDD[2][2][1]': mpf('-3.95193536311042053355897439286505'), 'betaU_dDD[2][2][2]': mpf('0.749372685256775991946308530989456')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__phi_and_derivs__globals'] = {'phi_dD[0]': mpf('-1.13647037353523967950954390927125'), 'phi_dD[1]': mpf('-1.37882238967032079487109408639122'), 'phi_dD[2]': mpf('-1.23865565471203051564572756374677'), 'phi_dupD[0]': mpf('-1.30437304716054772824033366627827'), 'phi_dupD[1]': mpf('-0.387439729438011521402787682064875'), 'phi_dupD[2]': mpf('-1.286536248178407847990777607347'), 'phi_dDD[0][0]': mpf('1.41350298031869520561660429447829'), 'phi_dDD[0][1]': mpf('2.60287661327406578686593533352924'), 'phi_dDD[0][2]': mpf('2.40978752502737233244962940835342'), 'phi_dDD[1][0]': mpf('2.60287661327406578686593533352924'), 'phi_dDD[1][1]': mpf('3.41285382262798172893551850424285'), 'phi_dDD[1][2]': mpf('3.0612083554957324225454264680395'), 'phi_dDD[2][0]': mpf('2.40978752502737233244962940835342'), 'phi_dDD[2][1]': mpf('3.0612083554957324225454264680395'), 'phi_dDD[2][2]': mpf('2.10516420696710843004822415147254'), 'exp_m4phi': mpf('0.113544429071223891132192393151795'), 'phi_dBarD[0]': mpf('-1.13647037353523967950954390927125'), 'phi_dBarD[1]': mpf('-1.37882238967032079487109408639122'), 'phi_dBarD[2]': mpf('-1.23865565471203051564572756374677'), 'phi_dBarDD[0][0]': mpf('236.194201114655255326559793477184'), 'phi_dBarDD[0][1]': mpf('95.1087520363466622986255911257178'), 'phi_dBarDD[0][2]': mpf('35.7199091574093810655803917372351'), 'phi_dBarDD[1][0]': mpf('95.1087520363466622986255911257178'), 'phi_dBarDD[1][1]': mpf('11.7211452920461024221333792078154'), 'phi_dBarDD[1][2]': mpf('-22.3188329171420787008183405055411'), 'phi_dBarDD[2][0]': mpf('35.7199091574093810655803917372351'), 'phi_dBarDD[2][1]': mpf('-22.3188329171420787008183405055411'), 'phi_dBarDD[2][2]': mpf('-18.8021112171325449817326637076808')}
# Generated on: 2019-09-03
trusted_values_dict['BrillLindquist__BrillLindquist__ComputeADMGlobalsOnly_True__globals'] = {'alphaCart': mpf('0.343480750893445797669295337903236'), 'betaCartU[0]': mpf('0.0'), 'betaCartU[1]': mpf('0.0'), 'betaCartU[2]': mpf('0.0'), 'BCartU[0]': mpf('0.0'), 'BCartU[1]': mpf('0.0'), 'BCartU[2]': mpf('0.0'), 'gammaCartDD[0][0]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[0][1]': mpf('0.0'), 'gammaCartDD[0][2]': mpf('0.0'), 'gammaCartDD[1][0]': mpf('0.0'), 'gammaCartDD[1][1]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[1][2]': mpf('0.0'), 'gammaCartDD[2][0]': mpf('0.0'), 'gammaCartDD[2][1]': mpf('0.0'), 'gammaCartDD[2][2]': mpf('8.47608284216415538664572854966934'), 'KCartDD[0][0]': mpf('0.0'), 'KCartDD[0][1]': mpf('0.0'), 'KCartDD[0][2]': mpf('0.0'), 'KCartDD[1][0]': mpf('0.0'), 'KCartDD[1][1]': mpf('0.0'), 'KCartDD[1][2]': mpf('0.0'), 'KCartDD[2][0]': mpf('0.0'), 'KCartDD[2][1]': mpf('0.0'), 'KCartDD[2][2]': mpf('0.0')}
# Generated on: 2019-09-03
trusted_values_dict['Psi4__Psi4__specify_tetrad_False__globals'] = {'psi4_re_pt[0]': mpf('-11.4627408385133346012203475168749'), 'psi4_re_pt[1]': mpf('-35.6806714906029645727316589574499'), 'psi4_re_pt[2]': mpf('-3374.92496104893019517395960329346'), 'psi4_im_pt[0]': mpf('7.17626585537563385139665194926662'), 'psi4_im_pt[1]': mpf('103.321768247585408574682983348206'), 'psi4_im_pt[2]': mpf('-1785.88265198482805325630868693525')}
# Generated on: 2019-09-03
trusted_values_dict['ShiftedKerrSchild__ShiftedKerrSchild__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('0.764992222074068488402374263959388'), 'betaSphU[0]': mpf('0.414786900166179080948286822502086'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('1.70877924688282501671697638255698'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.83749814889950038479162273698221'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.428211355345928897234853289336263'), 'KSphDD[0][0]': mpf('-0.608416975836176206520434449342952'), 'KSphDD[0][1]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[0][2]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[1][0]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[1][1]': mpf('0.457311525223526834585984744200641'), 'KSphDD[1][2]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][0]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[2][1]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][2]': mpf('0.189536302720822065090385756720081')}
# Generated on: 2019-09-03
trusted_values_dict['StaticTrumpet__StaticTrumpet__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('0.47901157071628237535174768234896'), 'betaSphU[0]': mpf('0.249559485836202384477547321977731'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('4.35820838377928180149222863568597'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.456231514782622374769913065861258'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.19552817796951759898685705719116'), 'KSphDD[0][0]': mpf('-3.36157982955715545881983739302993'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.0'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.351901176504010315682080545229837'), 'KSphDD[1][2]': mpf('0.0'), 'KSphDD[2][0]': mpf('0.0'), 'KSphDD[2][1]': mpf('0.0'), 'KSphDD[2][2]': mpf('0.150815087598546495698285048292878')}
# Generated on: 2019-09-03
trusted_values_dict['UIUCBlackHole__UIUCBlackHole__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('1.0'), 'betaSphU[0]': mpf('0.0'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('5.88800397535091508054696345959799'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.49579570354855419371061261468577'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.239531368774223457427342816780148'), 'KSphDD[0][0]': mpf('0.0'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.516384223238703786717270771044344'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.0'), 'KSphDD[1][2]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][0]': mpf('0.516384223238703786717270771044344'), 'KSphDD[2][1]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][2]': mpf('0.0')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4DD_ito_BSSN_or_ADM___ADM___globals'] = {'g4DD[0][0]': mpf('1.42770464273047624140299713522594'), 'g4DD[0][1]': mpf('0.813388473397507463814385913308194'), 'g4DD[0][2]': mpf('0.652706348793296836714132090802973'), 'g4DD[0][3]': mpf('1.22429414375154980405074869244046'), 'g4DD[1][0]': mpf('0.813388473397507463814385913308194'), 'g4DD[1][1]': mpf('0.657497767033916602485987823456526'), 'g4DD[1][2]': mpf('0.0577387051674528306577371949970257'), 'g4DD[1][3]': mpf('0.391026617743468030141684721456841'), 'g4DD[2][0]': mpf('0.652706348793296836714132090802973'), 'g4DD[2][1]': mpf('0.0577387051674528306577371949970257'), 'g4DD[2][2]': mpf('0.14235077874207879844448143558111'), 'g4DD[2][3]': mpf('0.723120760610660329170684690325288'), 'g4DD[3][0]': mpf('1.22429414375154980405074869244046'), 'g4DD[3][1]': mpf('0.391026617743468030141684721456841'), 'g4DD[3][2]': mpf('0.723120760610660329170684690325288'), 'g4DD[3][3]': mpf('0.919283767179900235255729512573453')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4UU_ito_BSSN_or_ADM___ADM___globals'] = {'g4UU[0][0]': mpf('-3.03008926847944211197781568780977'), 'g4UU[0][1]': mpf('2.25487680174746330097911429618154'), 'g4UU[0][2]': mpf('0.88396408821931067329277382962707'), 'g4UU[0][3]': mpf('2.38097417962378184338842987036543'), 'g4UU[1][0]': mpf('2.25487680174746330097911429618154'), 'g4UU[1][1]': mpf('-0.109478866681308257858746913532804'), 'g4UU[1][2]': mpf('-1.57673807708112257475456728613969'), 'g4UU[1][3]': mpf('-1.71617440778374167644096697698262'), 'g4UU[2][0]': mpf('0.88396408821931067329277382962707'), 'g4UU[2][1]': mpf('-1.57673807708112257475456728613969'), 'g4UU[2][2]': mpf('-2.06437348264308527564608946533953'), 'g4UU[2][3]': mpf('1.11728919891515454886216209391363'), 'g4UU[3][0]': mpf('2.38097417962378184338842987036543'), 'g4UU[3][1]': mpf('-1.71617440778374167644096697698262'), 'g4UU[3][2]': mpf('1.11728919891515454886216209391363'), 'g4UU[3][3]': mpf('-2.23203972375107587678882970577398')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__BSSN_or_ADM_ito_g4DD___ADM___globals'] = {'gammaDD[0][0]': mpf('0.498904274895148658330867874610703'), 'gammaDD[0][1]': mpf('0.252497897757109623917415319738211'), 'gammaDD[0][2]': mpf('0.501283702587057011790250271587865'), 'gammaDD[1][0]': mpf('0.252497897757109623917415319738211'), 'gammaDD[1][1]': mpf('0.974132307468977720610325832240051'), 'gammaDD[1][2]': mpf('0.788410581529429888014703919907333'), 'gammaDD[2][0]': mpf('0.501283702587057011790250271587865'), 'gammaDD[2][1]': mpf('0.788410581529429888014703919907333'), 'gammaDD[2][2]': mpf('0.909148885640877257863223803724395'), 'betaU[0]': mpf('9.06694959302922007806468735643867'), 'betaU[1]': mpf('6.98967266398671524153764426039564'), 'betaU[2]': mpf('-10.826763784327769115585618700949'), 'alpha': mpf('2.9281812371251576982173791733214')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4DD_ito_BSSN_or_ADM___BSSN___globals'] = {'g4DD[0][0]': mpf('33.9316040782510563053933745757657'), 'g4DD[0][1]': mpf('11.962817515445852455324947838214'), 'g4DD[0][2]': mpf('8.00807061586371404752271503878887'), 'g4DD[0][3]': mpf('3.0744393577022965271968636933869'), 'g4DD[1][0]': mpf('11.962817515445852455324947838214'), 'g4DD[1][1]': mpf('8.84684305820134422150597858207921'), 'g4DD[1][2]': mpf('3.350018689599008046165002833204'), 'g4DD[1][3]': mpf('0.194795767378908960688399288371257'), 'g4DD[2][0]': mpf('8.00807061586371404752271503878887'), 'g4DD[2][1]': mpf('3.350018689599008046165002833204'), 'g4DD[2][2]': mpf('1.92752387150314689143948714387062'), 'g4DD[2][3]': mpf('0.617514215490828029162196536408385'), 'g4DD[3][0]': mpf('3.0744393577022965271968636933869'), 'g4DD[3][1]': mpf('0.194795767378908960688399288371257'), 'g4DD[3][2]': mpf('0.617514215490828029162196536408385'), 'g4DD[3][3]': mpf('0.440219517017598275317485123438443')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4UU_ito_BSSN_or_ADM___BSSN___globals'] = {'g4UU[0][0]': mpf('-3.03008926847944211197781568780977'), 'g4UU[0][1]': mpf('2.11077129805143422596450042497429'), 'g4UU[0][2]': mpf('4.4314190799497963375295703643971'), 'g4UU[0][3]': mpf('14.0116280048673688065179881220934'), 'g4UU[1][0]': mpf('2.11077129805143422596450042497429'), 'g4UU[1][1]': mpf('-7.75975232758250248342057473161043'), 'g4UU[1][2]': mpf('15.146151050341286192891709193053'), 'g4UU[1][3]': mpf('-32.5538384239792224249547239894336'), 'g4UU[2][0]': mpf('4.4314190799497963375295703643971'), 'g4UU[2][1]': mpf('15.146151050341286192891709193053'), 'g4UU[2][2]': mpf('-58.3968465178062189938977785341917'), 'g4UU[2][3]': mpf('44.2650695045030422092937754418837'), 'g4UU[3][0]': mpf('14.0116280048673688065179881220934'), 'g4UU[3][1]': mpf('-32.5538384239792224249547239894336'), 'g4UU[3][2]': mpf('44.2650695045030422092937754418837'), 'g4UU[3][3]': mpf('-143.271385972918464696654248758534')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__BSSN_or_ADM_ito_g4DD___BSSN___globals'] = {'hDD[0][0]': mpf('-0.707823822038679430924860360088691'), 'hDD[0][1]': mpf('0.359019871168498611582168252151222'), 'hDD[0][2]': mpf('1.59629921830030516363312220121092'), 'hDD[1][0]': mpf('0.359019871168498611582168252151222'), 'hDD[1][1]': mpf('2.36288339604623268350851551662764'), 'hDD[1][2]': mpf('6.09559777405440062117804245902213'), 'hDD[2][0]': mpf('1.59629921830030516363312220121092'), 'hDD[2][1]': mpf('6.09559777405440062117804245902213'), 'hDD[2][2]': mpf('14.7423246171918635049100138961082'), 'cf': mpf('0.765268415331718015781674002095462'), 'vetU[0]': mpf('9.06694959302922007806468735643867'), 'vetU[1]': mpf('2.87888088365405202711159845187769'), 'vetU[2]': mpf('-1.99111124153992461429422443718632'), 'alpha': mpf('2.9281812371251576982173791733214')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars___ADM___globals'] = {'SDD[0][0]': mpf('1.80029784665212934056799078226269'), 'SDD[0][1]': mpf('1.39659583030126451047804795612856'), 'SDD[0][2]': mpf('3.00811244695977724176245476191804'), 'SDD[1][0]': mpf('1.39659583030126451047804795612856'), 'SDD[1][1]': mpf('1.22494659231382375810150001831715'), 'SDD[1][2]': mpf('2.50094687070838058316104348117154'), 'SDD[2][0]': mpf('3.00811244695977724176245476191804'), 'SDD[2][1]': mpf('2.50094687070838058316104348117154'), 'SDD[2][2]': mpf('5.32511755805920028174659119741427'), 'SD[0]': mpf('0.608341733349877488954095315599777'), 'SD[1]': mpf('0.470565344820866964820125587278172'), 'SD[2]': mpf('1.12342071849067535252360437426405'), 'S': mpf('5.51886985348101528899497640423697'), 'rho': mpf('0.128468170910047767321502802016049')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars___BSSN___globals'] = {'SDD[0][0]': mpf('340.54483250614325553458020093047'), 'SDD[0][1]': mpf('193.449779818371076633319750276679'), 'SDD[0][2]': mpf('59.7673665437832411953316595668582'), 'SDD[1][0]': mpf('193.449779818371076633319750276679'), 'SDD[1][1]': mpf('109.367007247314914388974035339596'), 'SDD[1][2]': mpf('33.5362683001178984597875112467598'), 'SDD[2][0]': mpf('59.7673665437832411953316595668582'), 'SDD[2][1]': mpf('33.5362683001178984597875112467598'), 'SDD[2][2]': mpf('10.1607088644086656351007264098463'), 'SD[0]': mpf('8.31158076395254821775662660137348'), 'SD[1]': mpf('4.46081177450846137199550370457638'), 'SD[2]': mpf('1.24975530738365002814654512794839'), 'S': mpf('56.0590719290101215802838899642879'), 'rho': mpf('0.128468170910047767321502802016049')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__BSSN_source_terms_for_BSSN_RHSs__globals'] = {'sourceterm_trK_rhs': mpf('34.481947192059905389281900539522'), 'sourceterm_a_rhsDD[0][0]': mpf('-24.4205232684807096387985845039797'), 'sourceterm_a_rhsDD[0][1]': mpf('-44.274626076606489686511882243972'), 'sourceterm_a_rhsDD[0][2]': mpf('-42.5329213202643698441006620646304'), 'sourceterm_a_rhsDD[1][0]': mpf('-44.274626076606489686511882243972'), 'sourceterm_a_rhsDD[1][1]': mpf('-60.2567558996127088470902295335831'), 'sourceterm_a_rhsDD[1][2]': mpf('-40.4716414381018667155368392378324'), 'sourceterm_a_rhsDD[2][0]': mpf('-42.5329213202643698441006620646304'), 'sourceterm_a_rhsDD[2][1]': mpf('-40.4716414381018667155368392378324'), 'sourceterm_a_rhsDD[2][2]': mpf('-7.97163159314560426073123653904653'), 'sourceterm_lambda_rhsU[0]': mpf('-12.4024163649413169626935281713898'), 'sourceterm_lambda_rhsU[1]': mpf('-7.90926902901598263785587714011605'), 'sourceterm_lambda_rhsU[2]': mpf('-5.32437139197028910868095764281183'), 'sourceterm_Lambdabar_rhsU[0]': mpf('-12.4024163649413169626935281713898'), 'sourceterm_Lambdabar_rhsU[1]': mpf('-19.2030180331952244454789590045448'), 'sourceterm_Lambdabar_rhsU[2]': mpf('-28.9515272468210212098860497720561')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__gammabarDD_hDD__gammaDD_None__globals'] = {'gammabarDD[0][0]': mpc(real='0.0934311617845273723048649117117748', imag='0.161827519220989063386539896782779'), 'gammabarDD[0][1]': mpc(real='0.00820473402984368653523272740812899', imag='0.0142110162022786069846436163288672'), 'gammabarDD[0][2]': mpc(real='0.0555653159846578106884074088611669', imag='0.0962419504240464018796075151840341'), 'gammabarDD[1][0]': mpc(real='0.00820473402984368653523272740812899', imag='0.0142110162022786069846436163288672'), 'gammabarDD[1][1]': mpc(real='0.0202282035098053057653455510944696', imag='0.035036276224825878278235080642844'), 'gammabarDD[1][2]': mpc(real='0.102756261940095613982570910138747', imag='0.177979066476101677185539529091329'), 'gammabarDD[2][0]': mpc(real='0.0555653159846578106884074088611669', imag='0.0962419504240464018796075151840341'), 'gammabarDD[2][1]': mpc(real='0.102756261940095613982570910138747', imag='0.177979066476101677185539529091329'), 'gammabarDD[2][2]': mpc(real='0.1306312426956797445587454831184', imag='0.226259949404778087922451845770411'), 'hDD[0][0]': mpc(real='-0.906568838215472627695135088288225', imag='0.161827519220989063386539896782779'), 'hDD[0][1]': mpc(real='0.0199203813847587431207220021178728', imag='0.0345031126645514094053446285670361'), 'hDD[0][2]': mpc(real='0.302139096107031490667793605098268', imag='0.523320265410314511278500049229478'), 'hDD[1][0]': mpc(real='0.0199203813847587431207220021178728', imag='0.0345031126645514094053446285670361'), 'hDD[1][1]': mpc(real='-0.880759552378340582201587949384702', imag='0.206530513597969589412173263553996'), 'hDD[1][2]': mpc(real='1.35657739510377672864649412076687', imag='2.34966097271918039623983531782869'), 'hDD[2][0]': mpc(real='0.302139096107031490667793605098268', imag='0.523320265410314511278500049229478'), 'hDD[2][1]': mpc(real='1.35657739510377672864649412076687', imag='2.34966097271918039623983531782869'), 'hDD[2][2]': mpc(real='2.86236517613411134419720838195644', imag='6.68981272244899649592753121396527')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__trK_AbarDD_aDD__gammaDD_None_KDD_None__globals'] = {'trK': mpf('1.2095765123256476836242095293423'), 'AbarDD[0][0]': mpc(real='0.0386623707004693575872700250783964', imag='0.0669651903942752452225661841112014'), 'AbarDD[0][1]': mpc(real='0.0197082770012822795779872109278585', imag='0.0341357370958621092471574343107932'), 'AbarDD[0][2]': mpc(real='-0.0116599231388074490306516750592891', imag='-0.0201955792887624809017754756723662'), 'AbarDD[1][0]': mpc(real='0.0197082770012822795779872109278585', imag='0.0341357370958621092471574343107932'), 'AbarDD[1][1]': mpc(real='0.0523135333085628650984233445342397', imag='0.0906096976138776738496360962926701'), 'AbarDD[1][2]': mpc(real='0.0274023107443551947048909767090663', imag='0.0474621944540137424839443269775074'), 'AbarDD[2][0]': mpc(real='-0.0116599231388074490306516750592891', imag='-0.0201955792887624809017754756723662'), 'AbarDD[2][1]': mpc(real='0.0274023107443551947048909767090663', imag='0.0474621944540137424839443269775074'), 'AbarDD[2][2]': mpc(real='0.0773121459494762602870920886743988', imag='0.133908564826673265324430417422263'), 'aDD[0][0]': mpc(real='0.0386623707004693575872700250783964', imag='0.0669651903942752452225661841112014'), 'aDD[0][1]': mpc(real='0.0478499842741998068773590091495862', imag='0.0828786039042858496683763291912328'), 'aDD[0][2]': mpc(real='-0.0634013966339984497144754982400627', imag='-0.109814440240911720403360618547595'), 'aDD[1][0]': mpc(real='0.0478499842741998068773590091495862', imag='0.0828786039042858496683763291912328'), 'aDD[1][1]': mpc(real='0.308375834035874929472953454023809', imag='0.534122612376563332681200790830189'), 'aDD[1][2]': mpc(real='0.361762433038608999247287556499941', imag='0.626590914292604606039560621866258'), 'aDD[2][0]': mpc(real='-0.0634013966339984497144754982400627', imag='-0.109814440240911720403360618547595'), 'aDD[2][1]': mpc(real='0.361762433038608999247287556499941', imag='0.626590914292604606039560621866258'), 'aDD[2][2]': mpc(real='2.28588302495977568895568765583448', imag='3.95926553938956793388115329435095')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__LambdabarU_lambdaU__exact_gammaDD__gammaDD_None__globals'] = {'LambdabarU[0]': mpc(real='-4.74783841908033465983862697612494', imag='8.22349736797463570781019370770082'), 'LambdabarU[1]': mpc(real='12.33620224866062287105705763679', imag='-21.3669290671256320024440356064588'), 'LambdabarU[2]': mpc(real='-15.5391046195871584956194055848755', imag='26.9145187052532079974298540037125'), 'lambdaU[0]': mpc(real='-4.74783841908033465983862697612494', imag='8.22349736797463570781019370770082'), 'lambdaU[1]': mpc(real='5.08098998877906549154204185470007', imag='-8.80053281331416137334144877968356'), 'lambdaU[2]': mpc(real='-2.85774091943449803920884733088315', imag='4.949752467329147975760861299932')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__cf_from_gammaDD__gammaDD_None__globals'] = {'cf': mpc(real='0.461683509914074474700385053438367', imag='-0.266553098729302162528398412177921')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__betU_vetU__betaU_None_BU_None__globals'] = {'vetU[0]': mpf('0.744161838795925789646901193918893'), 'vetU[1]': mpf('0.120156164503744952492307520976668'), 'vetU[2]': mpf('0.144509409106853504997322208612039'), 'betU[0]': mpf('0.53440111094236486533048946512281'), 'betU[1]': mpf('0.216739633922006111369610208272842'), 'betU[2]': mpf('0.00830060552287044152590411578016158')}
# This module provides functions for setting up Curvilinear boundary conditions,
# as documented in Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# First we import needed core NRPy+ modules
from outputC import * # NRPy+: Core C code output module
import NRPy_param_funcs as par # NRPy+: Parameter interface
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
import shutil, os, sys # Standard Python modules for multiplatform OS-level functions
def Set_up_CurviBoundaryConditions(Ccodesdir,verbose=True,Cparamspath=os.path.join("../"),
enable_copy_of_static_Ccodes=True):
# Step P0: Check that Ccodesdir is not the same as CurviBoundaryConditions/boundary_conditions,
# to prevent trusted versions of these C codes from becoming contaminated.
if os.path.join(Ccodesdir) == os.path.join("CurviBoundaryConditions", "boundary_conditions"):
print("Error: Tried to output boundary conditions C code into CurviBoundaryConditions/boundary_conditions,"
" which is not allowed, to prevent trusted versions of these C codes from becoming contaminated.")
sys.exit(1)
# Step P1: Create the C codes output directory & copy static CurviBC files
# from CurviBoundaryConditions/boundary_conditions to Ccodesdir/
if enable_copy_of_static_Ccodes:
cmd.mkdir(os.path.join(Ccodesdir))
for file in ["apply_bcs_curvilinear.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h",
"driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h"]:
shutil.copy(os.path.join("nrpytutorial", "CurviBoundaryConditions", "boundary_conditions", file),
os.path.join(Ccodesdir))
# Step P2: Output correct #include for set_Cparameters.h to
# Ccodesdir/boundary_conditions/RELATIVE_PATH__set_Cparameters.h
with open(os.path.join(Ccodesdir, "RELATIVE_PATH__set_Cparameters.h"), "w") as file:
file.write("#include \"" + Cparamspath + "/set_Cparameters.h\"\n") # #include's may include forward slashes for paths, even in Windows.
# Step 0: Set up reference metric in case it hasn't already been set up.
# (Doing it twice hurts nothing).
rfm.reference_metric()
# Step 1: Set unit-vector dot products (=parity) for each of the 10 parity condition types
parity = ixp.zerorank1(DIM=10)
UnitVectors_inner = ixp.zerorank2()
xx0_inbounds, xx1_inbounds, xx2_inbounds = sp.symbols("xx0_inbounds xx1_inbounds xx2_inbounds", real=True)
for i in range(3):
for j in range(3):
UnitVectors_inner[i][j] = rfm.UnitVectors[i][j].subs(rfm.xx[0], xx0_inbounds).subs(rfm.xx[1],
xx1_inbounds).subs(
rfm.xx[2], xx2_inbounds)
# Type 0: scalar
parity[0] = sp.sympify(1)
# Type 1: i0-direction vector or one-form
# Type 2: i1-direction vector or one-form
# Type 3: i2-direction vector or one-form
for i in range(3):
for Type in range(1, 4):
parity[Type] += rfm.UnitVectors[Type - 1][i] * UnitVectors_inner[Type - 1][i]
# Type 4: i0i0-direction rank-2 tensor
# parity[4] = parity[1]*parity[1]
# Type 5: i0i1-direction rank-2 tensor
# Type 6: i0i2-direction rank-2 tensor
# Type 7: i1i1-direction rank-2 tensor
# Type 8: i1i2-direction rank-2 tensor
# Type 9: i2i2-direction rank-2 tensor
count = 4
for i in range(3):
for j in range(i, 3):
parity[count] = parity[i + 1] * parity[j + 1]
count = count + 1
lhs_strings = []
for i in range(10):
lhs_strings.append("parity[" + str(i) + "]")
outputC(parity, lhs_strings, os.path.join(Ccodesdir, "parity_conditions_symbolic_dot_products.h"))
# Step 2.a: Generate Ccodesdir/gridfunction_defines.h file,
# containing human-readable gridfunction aliases
evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gri.output__gridfunction_defines_h__return_gf_lists(Ccodesdir)
# Step 2.b: set the parity conditions on all gridfunctions in gf_list,
# based on how many digits are at the end of their names
def set_parity_types(list_of_gf_names):
parity_type = []
for name in list_of_gf_names:
for gf in gri.glb_gridfcs_list:
if gf.name == name:
parity_type__orig_len = len(parity_type)
if gf.DIM < 3 or gf.DIM > 4:
print("Error: Cannot currently specify parity conditions on gridfunctions with DIM<3 or >4.")
sys.exit(1)
if gf.rank == 0:
parity_type.append(0)
elif gf.rank == 1:
if gf.DIM == 3:
parity_type.append(int(gf.name[-1]) + 1) # = 1 for e.g., beta^0; = 2 for e.g., beta^1, etc.
elif gf.DIM == 4:
parity_type.append(int(gf.name[-1])) # = 0 for e.g., b4^0; = 1 for e.g., beta^1, etc.
elif gf.rank == 2:
if gf.DIM == 3:
# element of a list; a[-2] the
# second-to-last element, etc.
idx0 = gf.name[-2]
idx1 = gf.name[-1]
if idx0 == "0" and idx1 == "0":
parity_type.append(4)
elif (idx0 == "0" and idx1 == "1") or (idx0 == "1" and idx1 == "0"):
parity_type.append(5)
elif (idx0 == "0" and idx1 == "2") or (idx0 == "2" and idx1 == "0"):
parity_type.append(6)
elif idx0 == "1" and idx1 == "1":
parity_type.append(7)
elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"):
parity_type.append(8)
elif idx0 == "2" and idx1 == "2":
parity_type.append(9)
elif gf.DIM == 4:
idx0 = gf.name[-2]
idx1 = gf.name[-1]
# g4DD00 = g_{tt} : parity type = 0
# g4DD01 = g_{tx} : parity type = 1
# g4DD02 = g_{ty} : parity type = 2
# g4DD0a = g_{ta} : parity type = a
if idx0 == "0":
parity_type.append(int(idx1))
elif idx1 == "0":
parity_type.append(int(idx0))
if idx0 == "1" and idx1 == "1":
parity_type.append(4)
elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"):
parity_type.append(5)
elif (idx0 == "1" and idx1 == "3") or (idx0 == "3" and idx1 == "1"):
parity_type.append(6)
elif idx0 == "2" and idx1 == "2":
parity_type.append(7)
elif (idx0 == "2" and idx1 == "3") or (idx0 == "3" and idx1 == "2"):
parity_type.append(8)
elif idx0 == "3" and idx1 == "3":
parity_type.append(9)
if len(parity_type) == parity_type__orig_len:
print("Error: Could not figure out parity type for "+gf.gftype+" gridfunction: " + gf.name,gf.DIM,gf.name[-2],gf.name[-1],gf.rank)
sys.exit(1)
if len(parity_type) != len(list_of_gf_names):
print("Error: For some reason the length of the parity types list did not match the length of the gf list.")
sys.exit(1)
return parity_type
evol_parity_type = set_parity_types(evolved_variables_list)
aux_parity_type = set_parity_types(auxiliary_variables_list)
auxevol_parity_type = set_parity_types(auxevol_variables_list)
# Step 2.c: Output all gridfunctions to Ccodesdir+"/gridfunction_defines.h"
# ... then append to the file the parity type for each gridfunction.
with open(os.path.join(Ccodesdir, "gridfunction_defines.h"), "a") as file:
file.write("\n\n/* PARITY TYPES FOR ALL GRIDFUNCTIONS.\n")
file.write(
" SEE \"Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb\" FOR DEFINITIONS. */\n")
if len(evolved_variables_list) > 0:
file.write("const int8_t evol_gf_parity[" + str(len(evolved_variables_list)) + "] = { ")
for i in range(len(evolved_variables_list) - 1):
file.write(str(evol_parity_type[i]) + ", ")
file.write(str(evol_parity_type[len(evolved_variables_list) - 1]) + " };\n")
if len(auxiliary_variables_list) > 0:
file.write("const int8_t aux_gf_parity[" + str(len(auxiliary_variables_list)) + "] = { ")
for i in range(len(auxiliary_variables_list) - 1):
file.write(str(aux_parity_type[i]) + ", ")
file.write(str(aux_parity_type[len(auxiliary_variables_list) - 1]) + " };\n")
if len(auxevol_variables_list) > 0:
file.write("const int8_t auxevol_gf_parity[" + str(len(auxevol_variables_list)) + "] = { ")
for i in range(len(auxevol_variables_list) - 1):
file.write(str(auxevol_parity_type[i]) + ", ")
file.write(str(auxevol_parity_type[len(auxevol_variables_list) - 1]) + " };\n")
if verbose == True:
for i in range(len(evolved_variables_list)):
print("Evolved gridfunction \"" + evolved_variables_list[i] + "\" has parity type " + str(
evol_parity_type[i]) + ".")
for i in range(len(auxiliary_variables_list)):
print("Auxiliary gridfunction \"" + auxiliary_variables_list[i] + "\" has parity type " + str(
aux_parity_type[i]) + ".")
for i in range(len(auxevol_variables_list)):
print("AuxEvol gridfunction \"" + auxevol_variables_list[i] + "\" has parity type " + str(
auxevol_parity_type[i]) + ".")
# Step 3: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric:
CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
par.set_parval_from_str("reference_metric::CoordSystem", rfm.get_EigenCoord())
rfm.reference_metric()
# Step 4: Output C code for the Eigen-Coordinate mapping from xx->Cartesian:
rfm.xxCart_h("EigenCoord_xxCart", os.path.join(Cparamspath,"set_Cparameters.h"), os.path.join(Ccodesdir, "EigenCoord_xxCart.h"))
# Step 5: Output the Eigen-Coordinate mapping from Cartesian->xx:
# Step 5.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not!
if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[2] == 0:
print("ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for")
print(" reference_metric::CoordSystem = " + par.parval_from_str("reference_metric::CoordSystem"))
print(" Boundary conditions in curvilinear coordinates REQUIRE this be set.")
sys.exit(1)
# Step 5.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx:
outputC([rfm.Cart_to_xx[0], rfm.Cart_to_xx[1], rfm.Cart_to_xx[2]],
["Cart_to_xx0_inbounds", "Cart_to_xx1_inbounds", "Cart_to_xx2_inbounds"],
os.path.join(Ccodesdir, "EigenCoord_Cart_to_xx.h"))
# Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem
par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig)
rfm.reference_metric()
7c7
< #include "Logo.c"
---
> //#include "Logo.c"
16c16
< printf("\x1B[32mID: %s, Evol: %s, Coords: %s, FD order: %d\x1B[0m\n", params.ID_scheme,params.Evol_scheme,params.CoordSystem, params.FDCENTERDERIVS_FDORDER);
---
> // printf("\x1B[32mID: %s, Evol: %s, Coords: %s, FD order: %d\x1B[0m\n", params.ID_scheme,params.Evol_scheme,params.CoordSystem, params.FDCENTERDERIVS_FDORDER);
189a190,205
> LOOP_GZFILL(ii,jj,kk) {
> int which_gf = 0;
> gfs_n[IDX4(VET1,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(VET2,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(VET3,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
>
> gfs_n[IDX4(A11,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(A12,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(A13,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(A22,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(A23,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> gfs_n[IDX4(A33,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
>
> gfs_n[IDX4(CF,ii,jj,kk)] = (REAL)IDX4(which_gf,ii,jj,kk); which_gf++;
> }
>
195a212,230
> LOOP_GZFILL(ii,jj,kk) {
> int which_gf = 0;
> printf("%d %d %d | ",ii,jj,kk);
> printf("%d ",(int)gfs_n[IDX4(VET1,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(VET2,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(VET3,ii,jj,kk)]);
>
> printf("%d ",(int)gfs_n[IDX4(A11,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(A12,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(A13,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(A22,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(A23,ii,jj,kk)]);
> printf("%d ",(int)gfs_n[IDX4(A33,ii,jj,kk)]);
>
> printf("%d ",(int)gfs_n[IDX4(CF,ii,jj,kk)]);
> printf("\n");
> }
> exit(0);
>
0 0 0 | -65 -1065 2065 3065 4065 -5065 6065 -7065 8065 9065
1 0 0 | -64 -1064 2064 3064 4064 -5064 6064 -7064 8064 9064
2 0 0 | -63 -1063 2063 3063 4063 -5063 6063 -7063 8063 9063
3 0 0 | 43 1043 2043 3043 4043 5043 6043 7043 8043 9043
4 0 0 | 44 1044 2044 3044 4044 5044 6044 7044 8044 9044
5 0 0 | 45 1045 2045 3045 4045 5045 6045 7045 8045 9045
6 0 0 | 46 1046 2046 3046 4046 5046 6046 7046 8046 9046
7 0 0 | 47 1047 2047 3047 4047 5047 6047 7047 8047 9047
8 0 0 | 48 1048 2048 3048 4048 5048 6048 7048 8048 9048
9 0 0 | 49 1049 2049 3049 4049 5049 6049 7049 8049 9049
0 1 0 | -35 -1035 2035 3035 4035 -5035 6035 -7035 8035 9035
1 1 0 | -34 -1034 2034 3034 4034 -5034 6034 -7034 8034 9034
2 1 0 | -33 -1033 2033 3033 4033 -5033 6033 -7033 8033 9033
3 1 0 | 53 1053 2053 3053 4053 5053 6053 7053 8053 9053
4 1 0 | 54 1054 2054 3054 4054 5054 6054 7054 8054 9054
5 1 0 | 55 1055 2055 3055 4055 5055 6055 7055 8055 9055
6 1 0 | 56 1056 2056 3056 4056 5056 6056 7056 8056 9056
7 1 0 | 57 1057 2057 3057 4057 5057 6057 7057 8057 9057
8 1 0 | 58 1058 2058 3058 4058 5058 6058 7058 8058 9058
9 1 0 | 59 1059 2059 3059 4059 5059 6059 7059 8059 9059
0 2 0 | -45 -1045 2045 3045 4045 -5045 6045 -7045 8045 9045
1 2 0 | -44 -1044 2044 3044 4044 -5044 6044 -7044 8044 9044
2 2 0 | -43 -1043 2043 3043 4043 -5043 6043 -7043 8043 9043
3 2 0 | 63 1063 2063 3063 4063 5063 6063 7063 8063 9063
4 2 0 | 64 1064 2064 3064 4064 5064 6064 7064 8064 9064
5 2 0 | 65 1065 2065 3065 4065 5065 6065 7065 8065 9065
6 2 0 | 66 1066 2066 3066 4066 5066 6066 7066 8066 9066
7 2 0 | 67 1067 2067 3067 4067 5067 6067 7067 8067 9067
8 2 0 | 68 1068 2068 3068 4068 5068 6068 7068 8068 9068
9 2 0 | 69 1069 2069 3069 4069 5069 6069 7069 8069 9069
0 3 0 | -55 -1055 2055 3055 4055 -5055 6055 -7055 8055 9055
1 3 0 | -54 -1054 2054 3054 4054 -5054 6054 -7054 8054 9054
2 3 0 | -53 -1053 2053 3053 4053 -5053 6053 -7053 8053 9053
3 3 0 | 33 1033 2033 3033 4033 5033 6033 7033 8033 9033
4 3 0 | 34 1034 2034 3034 4034 5034 6034 7034 8034 9034
5 3 0 | 35 1035 2035 3035 4035 5035 6035 7035 8035 9035
6 3 0 | 36 1036 2036 3036 4036 5036 6036 7036 8036 9036
7 3 0 | 37 1037 2037 3037 4037 5037 6037 7037 8037 9037
8 3 0 | 38 1038 2038 3038 4038 5038 6038 7038 8038 9038
9 3 0 | 39 1039 2039 3039 4039 5039 6039 7039 8039 9039
0 4 0 | -65 -1065 2065 3065 4065 -5065 6065 -7065 8065 9065
1 4 0 | -64 -1064 2064 3064 4064 -5064 6064 -7064 8064 9064
2 4 0 | -63 -1063 2063 3063 4063 -5063 6063 -7063 8063 9063
3 4 0 | 43 1043 2043 3043 4043 5043 6043 7043 8043 9043
4 4 0 | 44 1044 2044 3044 4044 5044 6044 7044 8044 9044
5 4 0 | 45 1045 2045 3045 4045 5045 6045 7045 8045 9045
6 4 0 | 46 1046 2046 3046 4046 5046 6046 7046 8046 9046
7 4 0 | 47 1047 2047 3047 4047 5047 6047 7047 8047 9047
8 4 0 | 48 1048 2048 3048 4048 5048 6048 7048 8048 9048
9 4 0 | 49 1049 2049 3049 4049 5049 6049 7049 8049 9049
0 5 0 | -35 -1035 2035 3035 4035 -5035 6035 -7035 8035 9035
1 5 0 | -34 -1034 2034 3034 4034 -5034 6034 -7034 8034 9034
2 5 0 | -33 -1033 2033 3033 4033 -5033 6033 -7033 8033 9033
3 5 0 | 53 1053 2053 3053 4053 5053 6053 7053 8053 9053
4 5 0 | 54 1054 2054 3054 4054 5054 6054 7054 8054 9054
5 5 0 | 55 1055 2055 3055 4055 5055 6055 7055 8055 9055
6 5 0 | 56 1056 2056 3056 4056 5056 6056 7056 8056 9056
7 5 0 | 57 1057 2057 3057 4057 5057 6057 7057 8057 9057
8 5 0 | 58 1058 2058 3058 4058 5058 6058 7058 8058 9058
9 5 0 | 59 1059 2059 3059 4059 5059 6059 7059 8059 9059
0 6 0 | -45 -1045 2045 3045 4045 -5045 6045 -7045 8045 9045
1 6 0 | -44 -1044 2044 3044 4044 -5044 6044 -7044 8044 9044
2 6 0 | -43 -1043 2043 3043 4043 -5043 6043 -7043 8043 9043
3 6 0 | 63 1063 2063 3063 4063 5063 6063 7063 8063 9063
4 6 0 | 64 1064 2064 3064 4064 5064 6064 7064 8064 9064
5 6 0 | 65 1065 2065 3065 4065 5065 6065 7065 8065 9065
6 6 0 | 66 1066 2066 3066 4066 5066 6066 7066 8066 9066
7 6 0 | 67 1067 2067 3067 4067 5067 6067 7067 8067 9067
8 6 0 | 68 1068 2068 3068 4068 5068 6068 7068 8068 9068
9 6 0 | 69 1069 2069 3069 4069 5069 6069 7069 8069 9069
0 7 0 | -55 -1055 2055 3055 4055 -5055 6055 -7055 8055 9055
1 7 0 | -54 -1054 2054 3054 4054 -5054 6054 -7054 8054 9054
2 7 0 | -53 -1053 2053 3053 4053 -5053 6053 -7053 8053 9053
3 7 0 | 33 1033 2033 3033 4033 5033 6033 7033 8033 9033
4 7 0 | 34 1034 2034 3034 4034 5034 6034 7034 8034 9034
5 7 0 | 35 1035 2035 3035 4035 5035 6035 7035 8035 9035
6 7 0 | 36 1036 2036 3036 4036 5036 6036 7036 8036 9036
7 7 0 | 37 1037 2037 3037 4037 5037 6037 7037 8037 9037
8 7 0 | 38 1038 2038 3038 4038 5038 6038 7038 8038 9038
9 7 0 | 39 1039 2039 3039 4039 5039 6039 7039 8039 9039
0 8 0 | -65 -1065 2065 3065 4065 -5065 6065 -7065 8065 9065
1 8 0 | -64 -1064 2064 3064 4064 -5064 6064 -7064 8064 9064
2 8 0 | -63 -1063 2063 3063 4063 -5063 6063 -7063 8063 9063
3 8 0 | 43 1043 2043 3043 4043 5043 6043 7043 8043 9043
4 8 0 | 44 1044 2044 3044 4044 5044 6044 7044 8044 9044
5 8 0 | 45 1045 2045 3045 4045 5045 6045 7045 8045 9045
6 8 0 | 46 1046 2046 3046 4046 5046 6046 7046 8046 9046
7 8 0 | 47 1047 2047 3047 4047 5047 6047 7047 8047 9047
8 8 0 | 48 1048 2048 3048 4048 5048 6048 7048 8048 9048
9 8 0 | 49 1049 2049 3049 4049 5049 6049 7049 8049 9049
0 9 0 | -35 -1035 2035 3035 4035 -5035 6035 -7035 8035 9035
1 9 0 | -34 -1034 2034 3034 4034 -5034 6034 -7034 8034 9034
2 9 0 | -33 -1033 2033 3033 4033 -5033 6033 -7033 8033 9033
3 9 0 | 53 1053 2053 3053 4053 5053 6053 7053 8053 9053
4 9 0 | 54 1054 2054 3054 4054 5054 6054 7054 8054 9054
5 9 0 | 55 1055 2055 3055 4055 5055 6055 7055 8055 9055
6 9 0 | 56 1056 2056 3056 4056 5056 6056 7056 8056 9056
7 9 0 | 57 1057 2057 3057 4057 5057 6057 7057 8057 9057
8 9 0 | 58 1058 2058 3058 4058 5058 6058 7058 8058 9058
9 9 0 | 59 1059 2059 3059 4059 5059 6059 7059 8059 9059
0 0 1 | -165 -1165 2165 3165 4165 -5165 6165 -7165 8165 9165
1 0 1 | -164 -1164 2164 3164 4164 -5164 6164 -7164 8164 9164
2 0 1 | -163 -1163 2163 3163 4163 -5163 6163 -7163 8163 9163
3 0 1 | 143 1143 2143 3143 4143 5143 6143 7143 8143 9143
4 0 1 | 144 1144 2144 3144 4144 5144 6144 7144 8144 9144
5 0 1 | 145 1145 2145 3145 4145 5145 6145 7145 8145 9145
6 0 1 | 146 1146 2146 3146 4146 5146 6146 7146 8146 9146
7 0 1 | 147 1147 2147 3147 4147 5147 6147 7147 8147 9147
8 0 1 | 148 1148 2148 3148 4148 5148 6148 7148 8148 9148
9 0 1 | 149 1149 2149 3149 4149 5149 6149 7149 8149 9149
0 1 1 | -135 -1135 2135 3135 4135 -5135 6135 -7135 8135 9135
1 1 1 | -134 -1134 2134 3134 4134 -5134 6134 -7134 8134 9134
2 1 1 | -133 -1133 2133 3133 4133 -5133 6133 -7133 8133 9133
3 1 1 | 153 1153 2153 3153 4153 5153 6153 7153 8153 9153
4 1 1 | 154 1154 2154 3154 4154 5154 6154 7154 8154 9154
5 1 1 | 155 1155 2155 3155 4155 5155 6155 7155 8155 9155
6 1 1 | 156 1156 2156 3156 4156 5156 6156 7156 8156 9156
7 1 1 | 157 1157 2157 3157 4157 5157 6157 7157 8157 9157
8 1 1 | 158 1158 2158 3158 4158 5158 6158 7158 8158 9158
9 1 1 | 159 1159 2159 3159 4159 5159 6159 7159 8159 9159
0 2 1 | -145 -1145 2145 3145 4145 -5145 6145 -7145 8145 9145
1 2 1 | -144 -1144 2144 3144 4144 -5144 6144 -7144 8144 9144
2 2 1 | -143 -1143 2143 3143 4143 -5143 6143 -7143 8143 9143
3 2 1 | 163 1163 2163 3163 4163 5163 6163 7163 8163 9163
4 2 1 | 164 1164 2164 3164 4164 5164 6164 7164 8164 9164
5 2 1 | 165 1165 2165 3165 4165 5165 6165 7165 8165 9165
6 2 1 | 166 1166 2166 3166 4166 5166 6166 7166 8166 9166
7 2 1 | 167 1167 2167 3167 4167 5167 6167 7167 8167 9167
8 2 1 | 168 1168 2168 3168 4168 5168 6168 7168 8168 9168
9 2 1 | 169 1169 2169 3169 4169 5169 6169 7169 8169 9169
0 3 1 | -155 -1155 2155 3155 4155 -5155 6155 -7155 8155 9155
1 3 1 | -154 -1154 2154 3154 4154 -5154 6154 -7154 8154 9154
2 3 1 | -153 -1153 2153 3153 4153 -5153 6153 -7153 8153 9153
3 3 1 | 133 1133 2133 3133 4133 5133 6133 7133 8133 9133
4 3 1 | 134 1134 2134 3134 4134 5134 6134 7134 8134 9134
5 3 1 | 135 1135 2135 3135 4135 5135 6135 7135 8135 9135
6 3 1 | 136 1136 2136 3136 4136 5136 6136 7136 8136 9136
7 3 1 | 137 1137 2137 3137 4137 5137 6137 7137 8137 9137
8 3 1 | 138 1138 2138 3138 4138 5138 6138 7138 8138 9138
9 3 1 | 139 1139 2139 3139 4139 5139 6139 7139 8139 9139
0 4 1 | -165 -1165 2165 3165 4165 -5165 6165 -7165 8165 9165
1 4 1 | -164 -1164 2164 3164 4164 -5164 6164 -7164 8164 9164
2 4 1 | -163 -1163 2163 3163 4163 -5163 6163 -7163 8163 9163
3 4 1 | 143 1143 2143 3143 4143 5143 6143 7143 8143 9143
4 4 1 | 144 1144 2144 3144 4144 5144 6144 7144 8144 9144
5 4 1 | 145 1145 2145 3145 4145 5145 6145 7145 8145 9145
6 4 1 | 146 1146 2146 3146 4146 5146 6146 7146 8146 9146
7 4 1 | 147 1147 2147 3147 4147 5147 6147 7147 8147 9147
8 4 1 | 148 1148 2148 3148 4148 5148 6148 7148 8148 9148
9 4 1 | 149 1149 2149 3149 4149 5149 6149 7149 8149 9149
0 5 1 | -135 -1135 2135 3135 4135 -5135 6135 -7135 8135 9135
1 5 1 | -134 -1134 2134 3134 4134 -5134 6134 -7134 8134 9134
2 5 1 | -133 -1133 2133 3133 4133 -5133 6133 -7133 8133 9133
3 5 1 | 153 1153 2153 3153 4153 5153 6153 7153 8153 9153
4 5 1 | 154 1154 2154 3154 4154 5154 6154 7154 8154 9154
5 5 1 | 155 1155 2155 3155 4155 5155 6155 7155 8155 9155
6 5 1 | 156 1156 2156 3156 4156 5156 6156 7156 8156 9156
7 5 1 | 157 1157 2157 3157 4157 5157 6157 7157 8157 9157
8 5 1 | 158 1158 2158 3158 4158 5158 6158 7158 8158 9158
9 5 1 | 159 1159 2159 3159 4159 5159 6159 7159 8159 9159
0 6 1 | -145 -1145 2145 3145 4145 -5145 6145 -7145 8145 9145
1 6 1 | -144 -1144 2144 3144 4144 -5144 6144 -7144 8144 9144
2 6 1 | -143 -1143 2143 3143 4143 -5143 6143 -7143 8143 9143
3 6 1 | 163 1163 2163 3163 4163 5163 6163 7163 8163 9163
4 6 1 | 164 1164 2164 3164 4164 5164 6164 7164 8164 9164
5 6 1 | 165 1165 2165 3165 4165 5165 6165 7165 8165 9165
6 6 1 | 166 1166 2166 3166 4166 5166 6166 7166 8166 9166
7 6 1 | 167 1167 2167 3167 4167 5167 6167 7167 8167 9167
8 6 1 | 168 1168 2168 3168 4168 5168 6168 7168 8168 9168
9 6 1 | 169 1169 2169 3169 4169 5169 6169 7169 8169 9169
0 7 1 | -155 -1155 2155 3155 4155 -5155 6155 -7155 8155 9155
1 7 1 | -154 -1154 2154 3154 4154 -5154 6154 -7154 8154 9154
2 7 1 | -153 -1153 2153 3153 4153 -5153 6153 -7153 8153 9153
3 7 1 | 133 1133 2133 3133 4133 5133 6133 7133 8133 9133
4 7 1 | 134 1134 2134 3134 4134 5134 6134 7134 8134 9134
5 7 1 | 135 1135 2135 3135 4135 5135 6135 7135 8135 9135
6 7 1 | 136 1136 2136 3136 4136 5136 6136 7136 8136 9136
7 7 1 | 137 1137 2137 3137 4137 5137 6137 7137 8137 9137
8 7 1 | 138 1138 2138 3138 4138 5138 6138 7138 8138 9138
9 7 1 | 139 1139 2139 3139 4139 5139 6139 7139 8139 9139
0 8 1 | -165 -1165 2165 3165 4165 -5165 6165 -7165 8165 9165
1 8 1 | -164 -1164 2164 3164 4164 -5164 6164 -7164 8164 9164
2 8 1 | -163 -1163 2163 3163 4163 -5163 6163 -7163 8163 9163
3 8 1 | 143 1143 2143 3143 4143 5143 6143 7143 8143 9143
4 8 1 | 144 1144 2144 3144 4144 5144 6144 7144 8144 9144
5 8 1 | 145 1145 2145 3145 4145 5145 6145 7145 8145 9145
6 8 1 | 146 1146 2146 3146 4146 5146 6146 7146 8146 9146
7 8 1 | 147 1147 2147 3147 4147 5147 6147 7147 8147 9147
8 8 1 | 148 1148 2148 3148 4148 5148 6148 7148 8148 9148
9 8 1 | 149 1149 2149 3149 4149 5149 6149 7149 8149 9149
0 9 1 | -135 -1135 2135 3135 4135 -5135 6135 -7135 8135 9135
1 9 1 | -134 -1134 2134 3134 4134 -5134 6134 -7134 8134 9134
2 9 1 | -133 -1133 2133 3133 4133 -5133 6133 -7133 8133 9133
3 9 1 | 153 1153 2153 3153 4153 5153 6153 7153 8153 9153
4 9 1 | 154 1154 2154 3154 4154 5154 6154 7154 8154 9154
5 9 1 | 155 1155 2155 3155 4155 5155 6155 7155 8155 9155
6 9 1 | 156 1156 2156 3156 4156 5156 6156 7156 8156 9156
7 9 1 | 157 1157 2157 3157 4157 5157 6157 7157 8157 9157
8 9 1 | 158 1158 2158 3158 4158 5158 6158 7158 8158 9158
9 9 1 | 159 1159 2159 3159 4159 5159 6159 7159 8159 9159
0 0 2 | -265 -1265 2265 3265 4265 -5265 6265 -7265 8265 9265
1 0 2 | -264 -1264 2264 3264 4264 -5264 6264 -7264 8264 9264
2 0 2 | -263 -1263 2263 3263 4263 -5263 6263 -7263 8263 9263
3 0 2 | 243 1243 2243 3243 4243 5243 6243 7243 8243 9243
4 0 2 | 244 1244 2244 3244 4244 5244 6244 7244 8244 9244
5 0 2 | 245 1245 2245 3245 4245 5245 6245 7245 8245 9245
6 0 2 | 246 1246 2246 3246 4246 5246 6246 7246 8246 9246
7 0 2 | 247 1247 2247 3247 4247 5247 6247 7247 8247 9247
8 0 2 | 248 1248 2248 3248 4248 5248 6248 7248 8248 9248
9 0 2 | 249 1249 2249 3249 4249 5249 6249 7249 8249 9249
0 1 2 | -235 -1235 2235 3235 4235 -5235 6235 -7235 8235 9235
1 1 2 | -234 -1234 2234 3234 4234 -5234 6234 -7234 8234 9234
2 1 2 | -233 -1233 2233 3233 4233 -5233 6233 -7233 8233 9233
3 1 2 | 253 1253 2253 3253 4253 5253 6253 7253 8253 9253
4 1 2 | 254 1254 2254 3254 4254 5254 6254 7254 8254 9254
5 1 2 | 255 1255 2255 3255 4255 5255 6255 7255 8255 9255
6 1 2 | 256 1256 2256 3256 4256 5256 6256 7256 8256 9256
7 1 2 | 257 1257 2257 3257 4257 5257 6257 7257 8257 9257
8 1 2 | 258 1258 2258 3258 4258 5258 6258 7258 8258 9258
9 1 2 | 259 1259 2259 3259 4259 5259 6259 7259 8259 9259
0 2 2 | -245 -1245 2245 3245 4245 -5245 6245 -7245 8245 9245
1 2 2 | -244 -1244 2244 3244 4244 -5244 6244 -7244 8244 9244
2 2 2 | -243 -1243 2243 3243 4243 -5243 6243 -7243 8243 9243
3 2 2 | 263 1263 2263 3263 4263 5263 6263 7263 8263 9263
4 2 2 | 264 1264 2264 3264 4264 5264 6264 7264 8264 9264
5 2 2 | 265 1265 2265 3265 4265 5265 6265 7265 8265 9265
6 2 2 | 266 1266 2266 3266 4266 5266 6266 7266 8266 9266
7 2 2 | 267 1267 2267 3267 4267 5267 6267 7267 8267 9267
8 2 2 | 268 1268 2268 3268 4268 5268 6268 7268 8268 9268
9 2 2 | 269 1269 2269 3269 4269 5269 6269 7269 8269 9269
0 3 2 | -255 -1255 2255 3255 4255 -5255 6255 -7255 8255 9255
1 3 2 | -254 -1254 2254 3254 4254 -5254 6254 -7254 8254 9254
2 3 2 | -253 -1253 2253 3253 4253 -5253 6253 -7253 8253 9253
3 3 2 | 233 1233 2233 3233 4233 5233 6233 7233 8233 9233
4 3 2 | 234 1234 2234 3234 4234 5234 6234 7234 8234 9234
5 3 2 | 235 1235 2235 3235 4235 5235 6235 7235 8235 9235
6 3 2 | 236 1236 2236 3236 4236 5236 6236 7236 8236 9236
7 3 2 | 237 1237 2237 3237 4237 5237 6237 7237 8237 9237
8 3 2 | 238 1238 2238 3238 4238 5238 6238 7238 8238 9238
9 3 2 | 239 1239 2239 3239 4239 5239 6239 7239 8239 9239
0 4 2 | -265 -1265 2265 3265 4265 -5265 6265 -7265 8265 9265
1 4 2 | -264 -1264 2264 3264 4264 -5264 6264 -7264 8264 9264
2 4 2 | -263 -1263 2263 3263 4263 -5263 6263 -7263 8263 9263
3 4 2 | 243 1243 2243 3243 4243 5243 6243 7243 8243 9243
4 4 2 | 244 1244 2244 3244 4244 5244 6244 7244 8244 9244
5 4 2 | 245 1245 2245 3245 4245 5245 6245 7245 8245 9245
6 4 2 | 246 1246 2246 3246 4246 5246 6246 7246 8246 9246
7 4 2 | 247 1247 2247 3247 4247 5247 6247 7247 8247 9247
8 4 2 | 248 1248 2248 3248 4248 5248 6248 7248 8248 9248
9 4 2 | 249 1249 2249 3249 4249 5249 6249 7249 8249 9249
0 5 2 | -235 -1235 2235 3235 4235 -5235 6235 -7235 8235 9235
1 5 2 | -234 -1234 2234 3234 4234 -5234 6234 -7234 8234 9234
2 5 2 | -233 -1233 2233 3233 4233 -5233 6233 -7233 8233 9233
3 5 2 | 253 1253 2253 3253 4253 5253 6253 7253 8253 9253
4 5 2 | 254 1254 2254 3254 4254 5254 6254 7254 8254 9254
5 5 2 | 255 1255 2255 3255 4255 5255 6255 7255 8255 9255
6 5 2 | 256 1256 2256 3256 4256 5256 6256 7256 8256 9256
7 5 2 | 257 1257 2257 3257 4257 5257 6257 7257 8257 9257
8 5 2 | 258 1258 2258 3258 4258 5258 6258 7258 8258 9258
9 5 2 | 259 1259 2259 3259 4259 5259 6259 7259 8259 9259
0 6 2 | -245 -1245 2245 3245 4245 -5245 6245 -7245 8245 9245
1 6 2 | -244 -1244 2244 3244 4244 -5244 6244 -7244 8244 9244
2 6 2 | -243 -1243 2243 3243 4243 -5243 6243 -7243 8243 9243
3 6 2 | 263 1263 2263 3263 4263 5263 6263 7263 8263 9263
4 6 2 | 264 1264 2264 3264 4264 5264 6264 7264 8264 9264
5 6 2 | 265 1265 2265 3265 4265 5265 6265 7265 8265 9265
6 6 2 | 266 1266 2266 3266 4266 5266 6266 7266 8266 9266
7 6 2 | 267 1267 2267 3267 4267 5267 6267 7267 8267 9267
8 6 2 | 268 1268 2268 3268 4268 5268 6268 7268 8268 9268
9 6 2 | 269 1269 2269 3269 4269 5269 6269 7269 8269 9269
0 7 2 | -255 -1255 2255 3255 4255 -5255 6255 -7255 8255 9255
1 7 2 | -254 -1254 2254 3254 4254 -5254 6254 -7254 8254 9254
2 7 2 | -253 -1253 2253 3253 4253 -5253 6253 -7253 8253 9253
3 7 2 | 233 1233 2233 3233 4233 5233 6233 7233 8233 9233
4 7 2 | 234 1234 2234 3234 4234 5234 6234 7234 8234 9234
5 7 2 | 235 1235 2235 3235 4235 5235 6235 7235 8235 9235
6 7 2 | 236 1236 2236 3236 4236 5236 6236 7236 8236 9236
7 7 2 | 237 1237 2237 3237 4237 5237 6237 7237 8237 9237
8 7 2 | 238 1238 2238 3238 4238 5238 6238 7238 8238 9238
9 7 2 | 239 1239 2239 3239 4239 5239 6239 7239 8239 9239
0 8 2 | -265 -1265 2265 3265 4265 -5265 6265 -7265 8265 9265
1 8 2 | -264 -1264 2264 3264 4264 -5264 6264 -7264 8264 9264
2 8 2 | -263 -1263 2263 3263 4263 -5263 6263 -7263 8263 9263
3 8 2 | 243 1243 2243 3243 4243 5243 6243 7243 8243 9243
4 8 2 | 244 1244 2244 3244 4244 5244 6244 7244 8244 9244
5 8 2 | 245 1245 2245 3245 4245 5245 6245 7245 8245 9245
6 8 2 | 246 1246 2246 3246 4246 5246 6246 7246 8246 9246
7 8 2 | 247 1247 2247 3247 4247 5247 6247 7247 8247 9247
8 8 2 | 248 1248 2248 3248 4248 5248 6248 7248 8248 9248
9 8 2 | 249 1249 2249 3249 4249 5249 6249 7249 8249 9249
0 9 2 | -235 -1235 2235 3235 4235 -5235 6235 -7235 8235 9235
1 9 2 | -234 -1234 2234 3234 4234 -5234 6234 -7234 8234 9234
2 9 2 | -233 -1233 2233 3233 4233 -5233 6233 -7233 8233 9233
3 9 2 | 253 1253 2253 3253 4253 5253 6253 7253 8253 9253
4 9 2 | 254 1254 2254 3254 4254 5254 6254 7254 8254 9254
5 9 2 | 255 1255 2255 3255 4255 5255 6255 7255 8255 9255
6 9 2 | 256 1256 2256 3256 4256 5256 6256 7256 8256 9256
7 9 2 | 257 1257 2257 3257 4257 5257 6257 7257 8257 9257
8 9 2 | 258 1258 2258 3258 4258 5258 6258 7258 8258 9258
9 9 2 | 259 1259 2259 3259 4259 5259 6259 7259 8259 9259
0 0 3 | -365 -1365 2365 3365 4365 -5365 6365 -7365 8365 9365
1 0 3 | -364 -1364 2364 3364 4364 -5364 6364 -7364 8364 9364
2 0 3 | -363 -1363 2363 3363 4363 -5363 6363 -7363 8363 9363
3 0 3 | 343 1343 2343 3343 4343 5343 6343 7343 8343 9343
4 0 3 | 344 1344 2344 3344 4344 5344 6344 7344 8344 9344
5 0 3 | 345 1345 2345 3345 4345 5345 6345 7345 8345 9345
6 0 3 | 346 1346 2346 3346 4346 5346 6346 7346 8346 9346
7 0 3 | 347 1347 2347 3347 4347 5347 6347 7347 8347 9347
8 0 3 | 348 1348 2348 3348 4348 5348 6348 7348 8348 9348
9 0 3 | 349 1349 2349 3349 4349 5349 6349 7349 8349 9349
0 1 3 | -335 -1335 2335 3335 4335 -5335 6335 -7335 8335 9335
1 1 3 | -334 -1334 2334 3334 4334 -5334 6334 -7334 8334 9334
2 1 3 | -333 -1333 2333 3333 4333 -5333 6333 -7333 8333 9333
3 1 3 | 353 1353 2353 3353 4353 5353 6353 7353 8353 9353
4 1 3 | 354 1354 2354 3354 4354 5354 6354 7354 8354 9354
5 1 3 | 355 1355 2355 3355 4355 5355 6355 7355 8355 9355
6 1 3 | 356 1356 2356 3356 4356 5356 6356 7356 8356 9356
7 1 3 | 357 1357 2357 3357 4357 5357 6357 7357 8357 9357
8 1 3 | 358 1358 2358 3358 4358 5358 6358 7358 8358 9358
9 1 3 | 359 1359 2359 3359 4359 5359 6359 7359 8359 9359
0 2 3 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 2 3 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 2 3 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 2 3 | 363 1363 2363 3363 4363 5363 6363 7363 8363 9363
4 2 3 | 364 1364 2364 3364 4364 5364 6364 7364 8364 9364
5 2 3 | 365 1365 2365 3365 4365 5365 6365 7365 8365 9365
6 2 3 | 366 1366 2366 3366 4366 5366 6366 7366 8366 9366
7 2 3 | 367 1367 2367 3367 4367 5367 6367 7367 8367 9367
8 2 3 | 368 1368 2368 3368 4368 5368 6368 7368 8368 9368
9 2 3 | 369 1369 2369 3369 4369 5369 6369 7369 8369 9369
0 3 3 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 3 3 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 3 3 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 3 3 | 333 1333 2333 3333 4333 5333 6333 7333 8333 9333
4 3 3 | 334 1334 2334 3334 4334 5334 6334 7334 8334 9334
5 3 3 | 335 1335 2335 3335 4335 5335 6335 7335 8335 9335
6 3 3 | 336 1336 2336 3336 4336 5336 6336 7336 8336 9336
7 3 3 | 337 1337 2337 3337 4337 5337 6337 7337 8337 9337
8 3 3 | 338 1338 2338 3338 4338 5338 6338 7338 8338 9338
9 3 3 | 339 1339 2339 3339 4339 5339 6339 7339 8339 9339
0 4 3 | -365 -1365 2365 3365 4365 -5365 6365 -7365 8365 9365
1 4 3 | -364 -1364 2364 3364 4364 -5364 6364 -7364 8364 9364
2 4 3 | -363 -1363 2363 3363 4363 -5363 6363 -7363 8363 9363
3 4 3 | 343 1343 2343 3343 4343 5343 6343 7343 8343 9343
4 4 3 | 344 1344 2344 3344 4344 5344 6344 7344 8344 9344
5 4 3 | 345 1345 2345 3345 4345 5345 6345 7345 8345 9345
6 4 3 | 346 1346 2346 3346 4346 5346 6346 7346 8346 9346
7 4 3 | 347 1347 2347 3347 4347 5347 6347 7347 8347 9347
8 4 3 | 348 1348 2348 3348 4348 5348 6348 7348 8348 9348
9 4 3 | 349 1349 2349 3349 4349 5349 6349 7349 8349 9349
0 5 3 | -335 -1335 2335 3335 4335 -5335 6335 -7335 8335 9335
1 5 3 | -334 -1334 2334 3334 4334 -5334 6334 -7334 8334 9334
2 5 3 | -333 -1333 2333 3333 4333 -5333 6333 -7333 8333 9333
3 5 3 | 353 1353 2353 3353 4353 5353 6353 7353 8353 9353
4 5 3 | 354 1354 2354 3354 4354 5354 6354 7354 8354 9354
5 5 3 | 355 1355 2355 3355 4355 5355 6355 7355 8355 9355
6 5 3 | 356 1356 2356 3356 4356 5356 6356 7356 8356 9356
7 5 3 | 357 1357 2357 3357 4357 5357 6357 7357 8357 9357
8 5 3 | 358 1358 2358 3358 4358 5358 6358 7358 8358 9358
9 5 3 | 359 1359 2359 3359 4359 5359 6359 7359 8359 9359
0 6 3 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 6 3 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 6 3 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 6 3 | 363 1363 2363 3363 4363 5363 6363 7363 8363 9363
4 6 3 | 364 1364 2364 3364 4364 5364 6364 7364 8364 9364
5 6 3 | 365 1365 2365 3365 4365 5365 6365 7365 8365 9365
6 6 3 | 366 1366 2366 3366 4366 5366 6366 7366 8366 9366
7 6 3 | 367 1367 2367 3367 4367 5367 6367 7367 8367 9367
8 6 3 | 368 1368 2368 3368 4368 5368 6368 7368 8368 9368
9 6 3 | 369 1369 2369 3369 4369 5369 6369 7369 8369 9369
0 7 3 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 7 3 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 7 3 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 7 3 | 333 1333 2333 3333 4333 5333 6333 7333 8333 9333
4 7 3 | 334 1334 2334 3334 4334 5334 6334 7334 8334 9334
5 7 3 | 335 1335 2335 3335 4335 5335 6335 7335 8335 9335
6 7 3 | 336 1336 2336 3336 4336 5336 6336 7336 8336 9336
7 7 3 | 337 1337 2337 3337 4337 5337 6337 7337 8337 9337
8 7 3 | 338 1338 2338 3338 4338 5338 6338 7338 8338 9338
9 7 3 | 339 1339 2339 3339 4339 5339 6339 7339 8339 9339
0 8 3 | -365 -1365 2365 3365 4365 -5365 6365 -7365 8365 9365
1 8 3 | -364 -1364 2364 3364 4364 -5364 6364 -7364 8364 9364
2 8 3 | -363 -1363 2363 3363 4363 -5363 6363 -7363 8363 9363
3 8 3 | 343 1343 2343 3343 4343 5343 6343 7343 8343 9343
4 8 3 | 344 1344 2344 3344 4344 5344 6344 7344 8344 9344
5 8 3 | 345 1345 2345 3345 4345 5345 6345 7345 8345 9345
6 8 3 | 346 1346 2346 3346 4346 5346 6346 7346 8346 9346
7 8 3 | 347 1347 2347 3347 4347 5347 6347 7347 8347 9347
8 8 3 | 348 1348 2348 3348 4348 5348 6348 7348 8348 9348
9 8 3 | 349 1349 2349 3349 4349 5349 6349 7349 8349 9349
0 9 3 | -335 -1335 2335 3335 4335 -5335 6335 -7335 8335 9335
1 9 3 | -334 -1334 2334 3334 4334 -5334 6334 -7334 8334 9334
2 9 3 | -333 -1333 2333 3333 4333 -5333 6333 -7333 8333 9333
3 9 3 | 353 1353 2353 3353 4353 5353 6353 7353 8353 9353
4 9 3 | 354 1354 2354 3354 4354 5354 6354 7354 8354 9354
5 9 3 | 355 1355 2355 3355 4355 5355 6355 7355 8355 9355
6 9 3 | 356 1356 2356 3356 4356 5356 6356 7356 8356 9356
7 9 3 | 357 1357 2357 3357 4357 5357 6357 7357 8357 9357
8 9 3 | 358 1358 2358 3358 4358 5358 6358 7358 8358 9358
9 9 3 | 359 1359 2359 3359 4359 5359 6359 7359 8359 9359
0 0 4 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 0 4 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 0 4 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 0 4 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 0 4 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 0 4 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 0 4 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 0 4 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 0 4 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 0 4 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 1 4 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 1 4 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 1 4 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 1 4 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 1 4 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 1 4 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 1 4 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 1 4 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 1 4 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 1 4 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 2 4 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 2 4 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 2 4 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 2 4 | 463 1463 2463 3463 4463 5463 6463 7463 8463 9463
4 2 4 | 464 1464 2464 3464 4464 5464 6464 7464 8464 9464
5 2 4 | 465 1465 2465 3465 4465 5465 6465 7465 8465 9465
6 2 4 | 466 1466 2466 3466 4466 5466 6466 7466 8466 9466
7 2 4 | 467 1467 2467 3467 4467 5467 6467 7467 8467 9467
8 2 4 | 468 1468 2468 3468 4468 5468 6468 7468 8468 9468
9 2 4 | 469 1469 2469 3469 4469 5469 6469 7469 8469 9469
0 3 4 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 3 4 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 3 4 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 3 4 | 433 1433 2433 3433 4433 5433 6433 7433 8433 9433
4 3 4 | 434 1434 2434 3434 4434 5434 6434 7434 8434 9434
5 3 4 | 435 1435 2435 3435 4435 5435 6435 7435 8435 9435
6 3 4 | 436 1436 2436 3436 4436 5436 6436 7436 8436 9436
7 3 4 | 437 1437 2437 3437 4437 5437 6437 7437 8437 9437
8 3 4 | 438 1438 2438 3438 4438 5438 6438 7438 8438 9438
9 3 4 | 439 1439 2439 3439 4439 5439 6439 7439 8439 9439
0 4 4 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 4 4 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 4 4 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 4 4 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 4 4 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 4 4 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 4 4 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 4 4 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 4 4 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 4 4 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 5 4 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 5 4 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 5 4 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 5 4 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 5 4 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 5 4 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 5 4 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 5 4 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 5 4 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 5 4 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 6 4 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 6 4 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 6 4 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 6 4 | 463 1463 2463 3463 4463 5463 6463 7463 8463 9463
4 6 4 | 464 1464 2464 3464 4464 5464 6464 7464 8464 9464
5 6 4 | 465 1465 2465 3465 4465 5465 6465 7465 8465 9465
6 6 4 | 466 1466 2466 3466 4466 5466 6466 7466 8466 9466
7 6 4 | 467 1467 2467 3467 4467 5467 6467 7467 8467 9467
8 6 4 | 468 1468 2468 3468 4468 5468 6468 7468 8468 9468
9 6 4 | 469 1469 2469 3469 4469 5469 6469 7469 8469 9469
0 7 4 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 7 4 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 7 4 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 7 4 | 433 1433 2433 3433 4433 5433 6433 7433 8433 9433
4 7 4 | 434 1434 2434 3434 4434 5434 6434 7434 8434 9434
5 7 4 | 435 1435 2435 3435 4435 5435 6435 7435 8435 9435
6 7 4 | 436 1436 2436 3436 4436 5436 6436 7436 8436 9436
7 7 4 | 437 1437 2437 3437 4437 5437 6437 7437 8437 9437
8 7 4 | 438 1438 2438 3438 4438 5438 6438 7438 8438 9438
9 7 4 | 439 1439 2439 3439 4439 5439 6439 7439 8439 9439
0 8 4 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 8 4 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 8 4 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 8 4 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 8 4 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 8 4 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 8 4 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 8 4 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 8 4 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 8 4 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 9 4 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 9 4 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 9 4 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 9 4 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 9 4 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 9 4 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 9 4 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 9 4 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 9 4 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 9 4 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 0 5 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 0 5 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 0 5 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 0 5 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 0 5 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 0 5 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 0 5 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 0 5 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 0 5 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 0 5 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 1 5 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 1 5 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 1 5 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 1 5 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 1 5 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 1 5 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 1 5 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 1 5 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 1 5 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 1 5 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 2 5 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 2 5 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 2 5 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 2 5 | 563 1563 2563 3563 4563 5563 6563 7563 8563 9563
4 2 5 | 564 1564 2564 3564 4564 5564 6564 7564 8564 9564
5 2 5 | 565 1565 2565 3565 4565 5565 6565 7565 8565 9565
6 2 5 | 566 1566 2566 3566 4566 5566 6566 7566 8566 9566
7 2 5 | 567 1567 2567 3567 4567 5567 6567 7567 8567 9567
8 2 5 | 568 1568 2568 3568 4568 5568 6568 7568 8568 9568
9 2 5 | 569 1569 2569 3569 4569 5569 6569 7569 8569 9569
0 3 5 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 3 5 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 3 5 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 3 5 | 533 1533 2533 3533 4533 5533 6533 7533 8533 9533
4 3 5 | 534 1534 2534 3534 4534 5534 6534 7534 8534 9534
5 3 5 | 535 1535 2535 3535 4535 5535 6535 7535 8535 9535
6 3 5 | 536 1536 2536 3536 4536 5536 6536 7536 8536 9536
7 3 5 | 537 1537 2537 3537 4537 5537 6537 7537 8537 9537
8 3 5 | 538 1538 2538 3538 4538 5538 6538 7538 8538 9538
9 3 5 | 539 1539 2539 3539 4539 5539 6539 7539 8539 9539
0 4 5 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 4 5 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 4 5 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 4 5 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 4 5 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 4 5 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 4 5 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 4 5 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 4 5 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 4 5 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 5 5 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 5 5 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 5 5 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 5 5 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 5 5 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 5 5 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 5 5 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 5 5 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 5 5 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 5 5 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 6 5 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 6 5 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 6 5 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 6 5 | 563 1563 2563 3563 4563 5563 6563 7563 8563 9563
4 6 5 | 564 1564 2564 3564 4564 5564 6564 7564 8564 9564
5 6 5 | 565 1565 2565 3565 4565 5565 6565 7565 8565 9565
6 6 5 | 566 1566 2566 3566 4566 5566 6566 7566 8566 9566
7 6 5 | 567 1567 2567 3567 4567 5567 6567 7567 8567 9567
8 6 5 | 568 1568 2568 3568 4568 5568 6568 7568 8568 9568
9 6 5 | 569 1569 2569 3569 4569 5569 6569 7569 8569 9569
0 7 5 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 7 5 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 7 5 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 7 5 | 533 1533 2533 3533 4533 5533 6533 7533 8533 9533
4 7 5 | 534 1534 2534 3534 4534 5534 6534 7534 8534 9534
5 7 5 | 535 1535 2535 3535 4535 5535 6535 7535 8535 9535
6 7 5 | 536 1536 2536 3536 4536 5536 6536 7536 8536 9536
7 7 5 | 537 1537 2537 3537 4537 5537 6537 7537 8537 9537
8 7 5 | 538 1538 2538 3538 4538 5538 6538 7538 8538 9538
9 7 5 | 539 1539 2539 3539 4539 5539 6539 7539 8539 9539
0 8 5 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 8 5 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 8 5 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 8 5 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 8 5 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 8 5 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 8 5 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 8 5 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 8 5 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 8 5 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 9 5 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 9 5 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 9 5 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 9 5 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 9 5 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 9 5 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 9 5 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 9 5 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 9 5 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 9 5 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 0 6 | -665 -1665 2665 3665 4665 -5665 6665 -7665 8665 9665
1 0 6 | -664 -1664 2664 3664 4664 -5664 6664 -7664 8664 9664
2 0 6 | -663 -1663 2663 3663 4663 -5663 6663 -7663 8663 9663
3 0 6 | 643 1643 2643 3643 4643 5643 6643 7643 8643 9643
4 0 6 | 644 1644 2644 3644 4644 5644 6644 7644 8644 9644
5 0 6 | 645 1645 2645 3645 4645 5645 6645 7645 8645 9645
6 0 6 | 646 1646 2646 3646 4646 5646 6646 7646 8646 9646
7 0 6 | 647 1647 2647 3647 4647 5647 6647 7647 8647 9647
8 0 6 | 648 1648 2648 3648 4648 5648 6648 7648 8648 9648
9 0 6 | 649 1649 2649 3649 4649 5649 6649 7649 8649 9649
0 1 6 | -635 -1635 2635 3635 4635 -5635 6635 -7635 8635 9635
1 1 6 | -634 -1634 2634 3634 4634 -5634 6634 -7634 8634 9634
2 1 6 | -633 -1633 2633 3633 4633 -5633 6633 -7633 8633 9633
3 1 6 | 653 1653 2653 3653 4653 5653 6653 7653 8653 9653
4 1 6 | 654 1654 2654 3654 4654 5654 6654 7654 8654 9654
5 1 6 | 655 1655 2655 3655 4655 5655 6655 7655 8655 9655
6 1 6 | 656 1656 2656 3656 4656 5656 6656 7656 8656 9656
7 1 6 | 657 1657 2657 3657 4657 5657 6657 7657 8657 9657
8 1 6 | 658 1658 2658 3658 4658 5658 6658 7658 8658 9658
9 1 6 | 659 1659 2659 3659 4659 5659 6659 7659 8659 9659
0 2 6 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 2 6 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 2 6 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 2 6 | 663 1663 2663 3663 4663 5663 6663 7663 8663 9663
4 2 6 | 664 1664 2664 3664 4664 5664 6664 7664 8664 9664
5 2 6 | 665 1665 2665 3665 4665 5665 6665 7665 8665 9665
6 2 6 | 666 1666 2666 3666 4666 5666 6666 7666 8666 9666
7 2 6 | 667 1667 2667 3667 4667 5667 6667 7667 8667 9667
8 2 6 | 668 1668 2668 3668 4668 5668 6668 7668 8668 9668
9 2 6 | 669 1669 2669 3669 4669 5669 6669 7669 8669 9669
0 3 6 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 3 6 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 3 6 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 3 6 | 633 1633 2633 3633 4633 5633 6633 7633 8633 9633
4 3 6 | 634 1634 2634 3634 4634 5634 6634 7634 8634 9634
5 3 6 | 635 1635 2635 3635 4635 5635 6635 7635 8635 9635
6 3 6 | 636 1636 2636 3636 4636 5636 6636 7636 8636 9636
7 3 6 | 637 1637 2637 3637 4637 5637 6637 7637 8637 9637
8 3 6 | 638 1638 2638 3638 4638 5638 6638 7638 8638 9638
9 3 6 | 639 1639 2639 3639 4639 5639 6639 7639 8639 9639
0 4 6 | -665 -1665 2665 3665 4665 -5665 6665 -7665 8665 9665
1 4 6 | -664 -1664 2664 3664 4664 -5664 6664 -7664 8664 9664
2 4 6 | -663 -1663 2663 3663 4663 -5663 6663 -7663 8663 9663
3 4 6 | 643 1643 2643 3643 4643 5643 6643 7643 8643 9643
4 4 6 | 644 1644 2644 3644 4644 5644 6644 7644 8644 9644
5 4 6 | 645 1645 2645 3645 4645 5645 6645 7645 8645 9645
6 4 6 | 646 1646 2646 3646 4646 5646 6646 7646 8646 9646
7 4 6 | 647 1647 2647 3647 4647 5647 6647 7647 8647 9647
8 4 6 | 648 1648 2648 3648 4648 5648 6648 7648 8648 9648
9 4 6 | 649 1649 2649 3649 4649 5649 6649 7649 8649 9649
0 5 6 | -635 -1635 2635 3635 4635 -5635 6635 -7635 8635 9635
1 5 6 | -634 -1634 2634 3634 4634 -5634 6634 -7634 8634 9634
2 5 6 | -633 -1633 2633 3633 4633 -5633 6633 -7633 8633 9633
3 5 6 | 653 1653 2653 3653 4653 5653 6653 7653 8653 9653
4 5 6 | 654 1654 2654 3654 4654 5654 6654 7654 8654 9654
5 5 6 | 655 1655 2655 3655 4655 5655 6655 7655 8655 9655
6 5 6 | 656 1656 2656 3656 4656 5656 6656 7656 8656 9656
7 5 6 | 657 1657 2657 3657 4657 5657 6657 7657 8657 9657
8 5 6 | 658 1658 2658 3658 4658 5658 6658 7658 8658 9658
9 5 6 | 659 1659 2659 3659 4659 5659 6659 7659 8659 9659
0 6 6 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 6 6 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 6 6 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 6 6 | 663 1663 2663 3663 4663 5663 6663 7663 8663 9663
4 6 6 | 664 1664 2664 3664 4664 5664 6664 7664 8664 9664
5 6 6 | 665 1665 2665 3665 4665 5665 6665 7665 8665 9665
6 6 6 | 666 1666 2666 3666 4666 5666 6666 7666 8666 9666
7 6 6 | 667 1667 2667 3667 4667 5667 6667 7667 8667 9667
8 6 6 | 668 1668 2668 3668 4668 5668 6668 7668 8668 9668
9 6 6 | 669 1669 2669 3669 4669 5669 6669 7669 8669 9669
0 7 6 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 7 6 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 7 6 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 7 6 | 633 1633 2633 3633 4633 5633 6633 7633 8633 9633
4 7 6 | 634 1634 2634 3634 4634 5634 6634 7634 8634 9634
5 7 6 | 635 1635 2635 3635 4635 5635 6635 7635 8635 9635
6 7 6 | 636 1636 2636 3636 4636 5636 6636 7636 8636 9636
7 7 6 | 637 1637 2637 3637 4637 5637 6637 7637 8637 9637
8 7 6 | 638 1638 2638 3638 4638 5638 6638 7638 8638 9638
9 7 6 | 639 1639 2639 3639 4639 5639 6639 7639 8639 9639
0 8 6 | -665 -1665 2665 3665 4665 -5665 6665 -7665 8665 9665
1 8 6 | -664 -1664 2664 3664 4664 -5664 6664 -7664 8664 9664
2 8 6 | -663 -1663 2663 3663 4663 -5663 6663 -7663 8663 9663
3 8 6 | 643 1643 2643 3643 4643 5643 6643 7643 8643 9643
4 8 6 | 644 1644 2644 3644 4644 5644 6644 7644 8644 9644
5 8 6 | 645 1645 2645 3645 4645 5645 6645 7645 8645 9645
6 8 6 | 646 1646 2646 3646 4646 5646 6646 7646 8646 9646
7 8 6 | 647 1647 2647 3647 4647 5647 6647 7647 8647 9647
8 8 6 | 648 1648 2648 3648 4648 5648 6648 7648 8648 9648
9 8 6 | 649 1649 2649 3649 4649 5649 6649 7649 8649 9649
0 9 6 | -635 -1635 2635 3635 4635 -5635 6635 -7635 8635 9635
1 9 6 | -634 -1634 2634 3634 4634 -5634 6634 -7634 8634 9634
2 9 6 | -633 -1633 2633 3633 4633 -5633 6633 -7633 8633 9633
3 9 6 | 653 1653 2653 3653 4653 5653 6653 7653 8653 9653
4 9 6 | 654 1654 2654 3654 4654 5654 6654 7654 8654 9654
5 9 6 | 655 1655 2655 3655 4655 5655 6655 7655 8655 9655
6 9 6 | 656 1656 2656 3656 4656 5656 6656 7656 8656 9656
7 9 6 | 657 1657 2657 3657 4657 5657 6657 7657 8657 9657
8 9 6 | 658 1658 2658 3658 4658 5658 6658 7658 8658 9658
9 9 6 | 659 1659 2659 3659 4659 5659 6659 7659 8659 9659
0 0 7 | -765 -1765 2765 3765 4765 -5765 6765 -7765 8765 9765
1 0 7 | -764 -1764 2764 3764 4764 -5764 6764 -7764 8764 9764
2 0 7 | -763 -1763 2763 3763 4763 -5763 6763 -7763 8763 9763
3 0 7 | 743 1743 2743 3743 4743 5743 6743 7743 8743 9743
4 0 7 | 744 1744 2744 3744 4744 5744 6744 7744 8744 9744
5 0 7 | 745 1745 2745 3745 4745 5745 6745 7745 8745 9745
6 0 7 | 746 1746 2746 3746 4746 5746 6746 7746 8746 9746
7 0 7 | 747 1747 2747 3747 4747 5747 6747 7747 8747 9747
8 0 7 | 748 1748 2748 3748 4748 5748 6748 7748 8748 9748
9 0 7 | 749 1749 2749 3749 4749 5749 6749 7749 8749 9749
0 1 7 | -735 -1735 2735 3735 4735 -5735 6735 -7735 8735 9735
1 1 7 | -734 -1734 2734 3734 4734 -5734 6734 -7734 8734 9734
2 1 7 | -733 -1733 2733 3733 4733 -5733 6733 -7733 8733 9733
3 1 7 | 753 1753 2753 3753 4753 5753 6753 7753 8753 9753
4 1 7 | 754 1754 2754 3754 4754 5754 6754 7754 8754 9754
5 1 7 | 755 1755 2755 3755 4755 5755 6755 7755 8755 9755
6 1 7 | 756 1756 2756 3756 4756 5756 6756 7756 8756 9756
7 1 7 | 757 1757 2757 3757 4757 5757 6757 7757 8757 9757
8 1 7 | 758 1758 2758 3758 4758 5758 6758 7758 8758 9758
9 1 7 | 759 1759 2759 3759 4759 5759 6759 7759 8759 9759
0 2 7 | -745 -1745 2745 3745 4745 -5745 6745 -7745 8745 9745
1 2 7 | -744 -1744 2744 3744 4744 -5744 6744 -7744 8744 9744
2 2 7 | -743 -1743 2743 3743 4743 -5743 6743 -7743 8743 9743
3 2 7 | 763 1763 2763 3763 4763 5763 6763 7763 8763 9763
4 2 7 | 764 1764 2764 3764 4764 5764 6764 7764 8764 9764
5 2 7 | 765 1765 2765 3765 4765 5765 6765 7765 8765 9765
6 2 7 | 766 1766 2766 3766 4766 5766 6766 7766 8766 9766
7 2 7 | 767 1767 2767 3767 4767 5767 6767 7767 8767 9767
8 2 7 | 768 1768 2768 3768 4768 5768 6768 7768 8768 9768
9 2 7 | 769 1769 2769 3769 4769 5769 6769 7769 8769 9769
0 3 7 | -755 -1755 2755 3755 4755 -5755 6755 -7755 8755 9755
1 3 7 | -754 -1754 2754 3754 4754 -5754 6754 -7754 8754 9754
2 3 7 | -753 -1753 2753 3753 4753 -5753 6753 -7753 8753 9753
3 3 7 | 733 1733 2733 3733 4733 5733 6733 7733 8733 9733
4 3 7 | 734 1734 2734 3734 4734 5734 6734 7734 8734 9734
5 3 7 | 735 1735 2735 3735 4735 5735 6735 7735 8735 9735
6 3 7 | 736 1736 2736 3736 4736 5736 6736 7736 8736 9736
7 3 7 | 737 1737 2737 3737 4737 5737 6737 7737 8737 9737
8 3 7 | 738 1738 2738 3738 4738 5738 6738 7738 8738 9738
9 3 7 | 739 1739 2739 3739 4739 5739 6739 7739 8739 9739
0 4 7 | -765 -1765 2765 3765 4765 -5765 6765 -7765 8765 9765
1 4 7 | -764 -1764 2764 3764 4764 -5764 6764 -7764 8764 9764
2 4 7 | -763 -1763 2763 3763 4763 -5763 6763 -7763 8763 9763
3 4 7 | 743 1743 2743 3743 4743 5743 6743 7743 8743 9743
4 4 7 | 744 1744 2744 3744 4744 5744 6744 7744 8744 9744
5 4 7 | 745 1745 2745 3745 4745 5745 6745 7745 8745 9745
6 4 7 | 746 1746 2746 3746 4746 5746 6746 7746 8746 9746
7 4 7 | 747 1747 2747 3747 4747 5747 6747 7747 8747 9747
8 4 7 | 748 1748 2748 3748 4748 5748 6748 7748 8748 9748
9 4 7 | 749 1749 2749 3749 4749 5749 6749 7749 8749 9749
0 5 7 | -735 -1735 2735 3735 4735 -5735 6735 -7735 8735 9735
1 5 7 | -734 -1734 2734 3734 4734 -5734 6734 -7734 8734 9734
2 5 7 | -733 -1733 2733 3733 4733 -5733 6733 -7733 8733 9733
3 5 7 | 753 1753 2753 3753 4753 5753 6753 7753 8753 9753
4 5 7 | 754 1754 2754 3754 4754 5754 6754 7754 8754 9754
5 5 7 | 755 1755 2755 3755 4755 5755 6755 7755 8755 9755
6 5 7 | 756 1756 2756 3756 4756 5756 6756 7756 8756 9756
7 5 7 | 757 1757 2757 3757 4757 5757 6757 7757 8757 9757
8 5 7 | 758 1758 2758 3758 4758 5758 6758 7758 8758 9758
9 5 7 | 759 1759 2759 3759 4759 5759 6759 7759 8759 9759
0 6 7 | -745 -1745 2745 3745 4745 -5745 6745 -7745 8745 9745
1 6 7 | -744 -1744 2744 3744 4744 -5744 6744 -7744 8744 9744
2 6 7 | -743 -1743 2743 3743 4743 -5743 6743 -7743 8743 9743
3 6 7 | 763 1763 2763 3763 4763 5763 6763 7763 8763 9763
4 6 7 | 764 1764 2764 3764 4764 5764 6764 7764 8764 9764
5 6 7 | 765 1765 2765 3765 4765 5765 6765 7765 8765 9765
6 6 7 | 766 1766 2766 3766 4766 5766 6766 7766 8766 9766
7 6 7 | 767 1767 2767 3767 4767 5767 6767 7767 8767 9767
8 6 7 | 768 1768 2768 3768 4768 5768 6768 7768 8768 9768
9 6 7 | 769 1769 2769 3769 4769 5769 6769 7769 8769 9769
0 7 7 | -755 -1755 2755 3755 4755 -5755 6755 -7755 8755 9755
1 7 7 | -754 -1754 2754 3754 4754 -5754 6754 -7754 8754 9754
2 7 7 | -753 -1753 2753 3753 4753 -5753 6753 -7753 8753 9753
3 7 7 | 733 1733 2733 3733 4733 5733 6733 7733 8733 9733
4 7 7 | 734 1734 2734 3734 4734 5734 6734 7734 8734 9734
5 7 7 | 735 1735 2735 3735 4735 5735 6735 7735 8735 9735
6 7 7 | 736 1736 2736 3736 4736 5736 6736 7736 8736 9736
7 7 7 | 737 1737 2737 3737 4737 5737 6737 7737 8737 9737
8 7 7 | 738 1738 2738 3738 4738 5738 6738 7738 8738 9738
9 7 7 | 739 1739 2739 3739 4739 5739 6739 7739 8739 9739
0 8 7 | -765 -1765 2765 3765 4765 -5765 6765 -7765 8765 9765
1 8 7 | -764 -1764 2764 3764 4764 -5764 6764 -7764 8764 9764
2 8 7 | -763 -1763 2763 3763 4763 -5763 6763 -7763 8763 9763
3 8 7 | 743 1743 2743 3743 4743 5743 6743 7743 8743 9743
4 8 7 | 744 1744 2744 3744 4744 5744 6744 7744 8744 9744
5 8 7 | 745 1745 2745 3745 4745 5745 6745 7745 8745 9745
6 8 7 | 746 1746 2746 3746 4746 5746 6746 7746 8746 9746
7 8 7 | 747 1747 2747 3747 4747 5747 6747 7747 8747 9747
8 8 7 | 748 1748 2748 3748 4748 5748 6748 7748 8748 9748
9 8 7 | 749 1749 2749 3749 4749 5749 6749 7749 8749 9749
0 9 7 | -735 -1735 2735 3735 4735 -5735 6735 -7735 8735 9735
1 9 7 | -734 -1734 2734 3734 4734 -5734 6734 -7734 8734 9734
2 9 7 | -733 -1733 2733 3733 4733 -5733 6733 -7733 8733 9733
3 9 7 | 753 1753 2753 3753 4753 5753 6753 7753 8753 9753
4 9 7 | 754 1754 2754 3754 4754 5754 6754 7754 8754 9754
5 9 7 | 755 1755 2755 3755 4755 5755 6755 7755 8755 9755
6 9 7 | 756 1756 2756 3756 4756 5756 6756 7756 8756 9756
7 9 7 | 757 1757 2757 3757 4757 5757 6757 7757 8757 9757
8 9 7 | 758 1758 2758 3758 4758 5758 6758 7758 8758 9758
9 9 7 | 759 1759 2759 3759 4759 5759 6759 7759 8759 9759
0 0 8 | -865 -1865 2865 3865 4865 -5865 6865 -7865 8865 9865
1 0 8 | -864 -1864 2864 3864 4864 -5864 6864 -7864 8864 9864
2 0 8 | -863 -1863 2863 3863 4863 -5863 6863 -7863 8863 9863
3 0 8 | 843 1843 2843 3843 4843 5843 6843 7843 8843 9843
4 0 8 | 844 1844 2844 3844 4844 5844 6844 7844 8844 9844
5 0 8 | 845 1845 2845 3845 4845 5845 6845 7845 8845 9845
6 0 8 | 846 1846 2846 3846 4846 5846 6846 7846 8846 9846
7 0 8 | 847 1847 2847 3847 4847 5847 6847 7847 8847 9847
8 0 8 | 848 1848 2848 3848 4848 5848 6848 7848 8848 9848
9 0 8 | 849 1849 2849 3849 4849 5849 6849 7849 8849 9849
0 1 8 | -835 -1835 2835 3835 4835 -5835 6835 -7835 8835 9835
1 1 8 | -834 -1834 2834 3834 4834 -5834 6834 -7834 8834 9834
2 1 8 | -833 -1833 2833 3833 4833 -5833 6833 -7833 8833 9833
3 1 8 | 853 1853 2853 3853 4853 5853 6853 7853 8853 9853
4 1 8 | 854 1854 2854 3854 4854 5854 6854 7854 8854 9854
5 1 8 | 855 1855 2855 3855 4855 5855 6855 7855 8855 9855
6 1 8 | 856 1856 2856 3856 4856 5856 6856 7856 8856 9856
7 1 8 | 857 1857 2857 3857 4857 5857 6857 7857 8857 9857
8 1 8 | 858 1858 2858 3858 4858 5858 6858 7858 8858 9858
9 1 8 | 859 1859 2859 3859 4859 5859 6859 7859 8859 9859
0 2 8 | -845 -1845 2845 3845 4845 -5845 6845 -7845 8845 9845
1 2 8 | -844 -1844 2844 3844 4844 -5844 6844 -7844 8844 9844
2 2 8 | -843 -1843 2843 3843 4843 -5843 6843 -7843 8843 9843
3 2 8 | 863 1863 2863 3863 4863 5863 6863 7863 8863 9863
4 2 8 | 864 1864 2864 3864 4864 5864 6864 7864 8864 9864
5 2 8 | 865 1865 2865 3865 4865 5865 6865 7865 8865 9865
6 2 8 | 866 1866 2866 3866 4866 5866 6866 7866 8866 9866
7 2 8 | 867 1867 2867 3867 4867 5867 6867 7867 8867 9867
8 2 8 | 868 1868 2868 3868 4868 5868 6868 7868 8868 9868
9 2 8 | 869 1869 2869 3869 4869 5869 6869 7869 8869 9869
0 3 8 | -855 -1855 2855 3855 4855 -5855 6855 -7855 8855 9855
1 3 8 | -854 -1854 2854 3854 4854 -5854 6854 -7854 8854 9854
2 3 8 | -853 -1853 2853 3853 4853 -5853 6853 -7853 8853 9853
3 3 8 | 833 1833 2833 3833 4833 5833 6833 7833 8833 9833
4 3 8 | 834 1834 2834 3834 4834 5834 6834 7834 8834 9834
5 3 8 | 835 1835 2835 3835 4835 5835 6835 7835 8835 9835
6 3 8 | 836 1836 2836 3836 4836 5836 6836 7836 8836 9836
7 3 8 | 837 1837 2837 3837 4837 5837 6837 7837 8837 9837
8 3 8 | 838 1838 2838 3838 4838 5838 6838 7838 8838 9838
9 3 8 | 839 1839 2839 3839 4839 5839 6839 7839 8839 9839
0 4 8 | -865 -1865 2865 3865 4865 -5865 6865 -7865 8865 9865
1 4 8 | -864 -1864 2864 3864 4864 -5864 6864 -7864 8864 9864
2 4 8 | -863 -1863 2863 3863 4863 -5863 6863 -7863 8863 9863
3 4 8 | 843 1843 2843 3843 4843 5843 6843 7843 8843 9843
4 4 8 | 844 1844 2844 3844 4844 5844 6844 7844 8844 9844
5 4 8 | 845 1845 2845 3845 4845 5845 6845 7845 8845 9845
6 4 8 | 846 1846 2846 3846 4846 5846 6846 7846 8846 9846
7 4 8 | 847 1847 2847 3847 4847 5847 6847 7847 8847 9847
8 4 8 | 848 1848 2848 3848 4848 5848 6848 7848 8848 9848
9 4 8 | 849 1849 2849 3849 4849 5849 6849 7849 8849 9849
0 5 8 | -835 -1835 2835 3835 4835 -5835 6835 -7835 8835 9835
1 5 8 | -834 -1834 2834 3834 4834 -5834 6834 -7834 8834 9834
2 5 8 | -833 -1833 2833 3833 4833 -5833 6833 -7833 8833 9833
3 5 8 | 853 1853 2853 3853 4853 5853 6853 7853 8853 9853
4 5 8 | 854 1854 2854 3854 4854 5854 6854 7854 8854 9854
5 5 8 | 855 1855 2855 3855 4855 5855 6855 7855 8855 9855
6 5 8 | 856 1856 2856 3856 4856 5856 6856 7856 8856 9856
7 5 8 | 857 1857 2857 3857 4857 5857 6857 7857 8857 9857
8 5 8 | 858 1858 2858 3858 4858 5858 6858 7858 8858 9858
9 5 8 | 859 1859 2859 3859 4859 5859 6859 7859 8859 9859
0 6 8 | -845 -1845 2845 3845 4845 -5845 6845 -7845 8845 9845
1 6 8 | -844 -1844 2844 3844 4844 -5844 6844 -7844 8844 9844
2 6 8 | -843 -1843 2843 3843 4843 -5843 6843 -7843 8843 9843
3 6 8 | 863 1863 2863 3863 4863 5863 6863 7863 8863 9863
4 6 8 | 864 1864 2864 3864 4864 5864 6864 7864 8864 9864
5 6 8 | 865 1865 2865 3865 4865 5865 6865 7865 8865 9865
6 6 8 | 866 1866 2866 3866 4866 5866 6866 7866 8866 9866
7 6 8 | 867 1867 2867 3867 4867 5867 6867 7867 8867 9867
8 6 8 | 868 1868 2868 3868 4868 5868 6868 7868 8868 9868
9 6 8 | 869 1869 2869 3869 4869 5869 6869 7869 8869 9869
0 7 8 | -855 -1855 2855 3855 4855 -5855 6855 -7855 8855 9855
1 7 8 | -854 -1854 2854 3854 4854 -5854 6854 -7854 8854 9854
2 7 8 | -853 -1853 2853 3853 4853 -5853 6853 -7853 8853 9853
3 7 8 | 833 1833 2833 3833 4833 5833 6833 7833 8833 9833
4 7 8 | 834 1834 2834 3834 4834 5834 6834 7834 8834 9834
5 7 8 | 835 1835 2835 3835 4835 5835 6835 7835 8835 9835
6 7 8 | 836 1836 2836 3836 4836 5836 6836 7836 8836 9836
7 7 8 | 837 1837 2837 3837 4837 5837 6837 7837 8837 9837
8 7 8 | 838 1838 2838 3838 4838 5838 6838 7838 8838 9838
9 7 8 | 839 1839 2839 3839 4839 5839 6839 7839 8839 9839
0 8 8 | -865 -1865 2865 3865 4865 -5865 6865 -7865 8865 9865
1 8 8 | -864 -1864 2864 3864 4864 -5864 6864 -7864 8864 9864
2 8 8 | -863 -1863 2863 3863 4863 -5863 6863 -7863 8863 9863
3 8 8 | 843 1843 2843 3843 4843 5843 6843 7843 8843 9843
4 8 8 | 844 1844 2844 3844 4844 5844 6844 7844 8844 9844
5 8 8 | 845 1845 2845 3845 4845 5845 6845 7845 8845 9845
6 8 8 | 846 1846 2846 3846 4846 5846 6846 7846 8846 9846
7 8 8 | 847 1847 2847 3847 4847 5847 6847 7847 8847 9847
8 8 8 | 848 1848 2848 3848 4848 5848 6848 7848 8848 9848
9 8 8 | 849 1849 2849 3849 4849 5849 6849 7849 8849 9849
0 9 8 | -835 -1835 2835 3835 4835 -5835 6835 -7835 8835 9835
1 9 8 | -834 -1834 2834 3834 4834 -5834 6834 -7834 8834 9834
2 9 8 | -833 -1833 2833 3833 4833 -5833 6833 -7833 8833 9833
3 9 8 | 853 1853 2853 3853 4853 5853 6853 7853 8853 9853
4 9 8 | 854 1854 2854 3854 4854 5854 6854 7854 8854 9854
5 9 8 | 855 1855 2855 3855 4855 5855 6855 7855 8855 9855
6 9 8 | 856 1856 2856 3856 4856 5856 6856 7856 8856 9856
7 9 8 | 857 1857 2857 3857 4857 5857 6857 7857 8857 9857
8 9 8 | 858 1858 2858 3858 4858 5858 6858 7858 8858 9858
9 9 8 | 859 1859 2859 3859 4859 5859 6859 7859 8859 9859
0 0 9 | -965 -1965 2965 3965 4965 -5965 6965 -7965 8965 9965
1 0 9 | -964 -1964 2964 3964 4964 -5964 6964 -7964 8964 9964
2 0 9 | -963 -1963 2963 3963 4963 -5963 6963 -7963 8963 9963
3 0 9 | 943 1943 2943 3943 4943 5943 6943 7943 8943 9943
4 0 9 | 944 1944 2944 3944 4944 5944 6944 7944 8944 9944
5 0 9 | 945 1945 2945 3945 4945 5945 6945 7945 8945 9945
6 0 9 | 946 1946 2946 3946 4946 5946 6946 7946 8946 9946
7 0 9 | 947 1947 2947 3947 4947 5947 6947 7947 8947 9947
8 0 9 | 948 1948 2948 3948 4948 5948 6948 7948 8948 9948
9 0 9 | 949 1949 2949 3949 4949 5949 6949 7949 8949 9949
0 1 9 | -935 -1935 2935 3935 4935 -5935 6935 -7935 8935 9935
1 1 9 | -934 -1934 2934 3934 4934 -5934 6934 -7934 8934 9934
2 1 9 | -933 -1933 2933 3933 4933 -5933 6933 -7933 8933 9933
3 1 9 | 953 1953 2953 3953 4953 5953 6953 7953 8953 9953
4 1 9 | 954 1954 2954 3954 4954 5954 6954 7954 8954 9954
5 1 9 | 955 1955 2955 3955 4955 5955 6955 7955 8955 9955
6 1 9 | 956 1956 2956 3956 4956 5956 6956 7956 8956 9956
7 1 9 | 957 1957 2957 3957 4957 5957 6957 7957 8957 9957
8 1 9 | 958 1958 2958 3958 4958 5958 6958 7958 8958 9958
9 1 9 | 959 1959 2959 3959 4959 5959 6959 7959 8959 9959
0 2 9 | -945 -1945 2945 3945 4945 -5945 6945 -7945 8945 9945
1 2 9 | -944 -1944 2944 3944 4944 -5944 6944 -7944 8944 9944
2 2 9 | -943 -1943 2943 3943 4943 -5943 6943 -7943 8943 9943
3 2 9 | 963 1963 2963 3963 4963 5963 6963 7963 8963 9963
4 2 9 | 964 1964 2964 3964 4964 5964 6964 7964 8964 9964
5 2 9 | 965 1965 2965 3965 4965 5965 6965 7965 8965 9965
6 2 9 | 966 1966 2966 3966 4966 5966 6966 7966 8966 9966
7 2 9 | 967 1967 2967 3967 4967 5967 6967 7967 8967 9967
8 2 9 | 968 1968 2968 3968 4968 5968 6968 7968 8968 9968
9 2 9 | 969 1969 2969 3969 4969 5969 6969 7969 8969 9969
0 3 9 | -955 -1955 2955 3955 4955 -5955 6955 -7955 8955 9955
1 3 9 | -954 -1954 2954 3954 4954 -5954 6954 -7954 8954 9954
2 3 9 | -953 -1953 2953 3953 4953 -5953 6953 -7953 8953 9953
3 3 9 | 933 1933 2933 3933 4933 5933 6933 7933 8933 9933
4 3 9 | 934 1934 2934 3934 4934 5934 6934 7934 8934 9934
5 3 9 | 935 1935 2935 3935 4935 5935 6935 7935 8935 9935
6 3 9 | 936 1936 2936 3936 4936 5936 6936 7936 8936 9936
7 3 9 | 937 1937 2937 3937 4937 5937 6937 7937 8937 9937
8 3 9 | 938 1938 2938 3938 4938 5938 6938 7938 8938 9938
9 3 9 | 939 1939 2939 3939 4939 5939 6939 7939 8939 9939
0 4 9 | -965 -1965 2965 3965 4965 -5965 6965 -7965 8965 9965
1 4 9 | -964 -1964 2964 3964 4964 -5964 6964 -7964 8964 9964
2 4 9 | -963 -1963 2963 3963 4963 -5963 6963 -7963 8963 9963
3 4 9 | 943 1943 2943 3943 4943 5943 6943 7943 8943 9943
4 4 9 | 944 1944 2944 3944 4944 5944 6944 7944 8944 9944
5 4 9 | 945 1945 2945 3945 4945 5945 6945 7945 8945 9945
6 4 9 | 946 1946 2946 3946 4946 5946 6946 7946 8946 9946
7 4 9 | 947 1947 2947 3947 4947 5947 6947 7947 8947 9947
8 4 9 | 948 1948 2948 3948 4948 5948 6948 7948 8948 9948
9 4 9 | 949 1949 2949 3949 4949 5949 6949 7949 8949 9949
0 5 9 | -935 -1935 2935 3935 4935 -5935 6935 -7935 8935 9935
1 5 9 | -934 -1934 2934 3934 4934 -5934 6934 -7934 8934 9934
2 5 9 | -933 -1933 2933 3933 4933 -5933 6933 -7933 8933 9933
3 5 9 | 953 1953 2953 3953 4953 5953 6953 7953 8953 9953
4 5 9 | 954 1954 2954 3954 4954 5954 6954 7954 8954 9954
5 5 9 | 955 1955 2955 3955 4955 5955 6955 7955 8955 9955
6 5 9 | 956 1956 2956 3956 4956 5956 6956 7956 8956 9956
7 5 9 | 957 1957 2957 3957 4957 5957 6957 7957 8957 9957
8 5 9 | 958 1958 2958 3958 4958 5958 6958 7958 8958 9958
9 5 9 | 959 1959 2959 3959 4959 5959 6959 7959 8959 9959
0 6 9 | -945 -1945 2945 3945 4945 -5945 6945 -7945 8945 9945
1 6 9 | -944 -1944 2944 3944 4944 -5944 6944 -7944 8944 9944
2 6 9 | -943 -1943 2943 3943 4943 -5943 6943 -7943 8943 9943
3 6 9 | 963 1963 2963 3963 4963 5963 6963 7963 8963 9963
4 6 9 | 964 1964 2964 3964 4964 5964 6964 7964 8964 9964
5 6 9 | 965 1965 2965 3965 4965 5965 6965 7965 8965 9965
6 6 9 | 966 1966 2966 3966 4966 5966 6966 7966 8966 9966
7 6 9 | 967 1967 2967 3967 4967 5967 6967 7967 8967 9967
8 6 9 | 968 1968 2968 3968 4968 5968 6968 7968 8968 9968
9 6 9 | 969 1969 2969 3969 4969 5969 6969 7969 8969 9969
0 7 9 | -955 -1955 2955 3955 4955 -5955 6955 -7955 8955 9955
1 7 9 | -954 -1954 2954 3954 4954 -5954 6954 -7954 8954 9954
2 7 9 | -953 -1953 2953 3953 4953 -5953 6953 -7953 8953 9953
3 7 9 | 933 1933 2933 3933 4933 5933 6933 7933 8933 9933
4 7 9 | 934 1934 2934 3934 4934 5934 6934 7934 8934 9934
5 7 9 | 935 1935 2935 3935 4935 5935 6935 7935 8935 9935
6 7 9 | 936 1936 2936 3936 4936 5936 6936 7936 8936 9936
7 7 9 | 937 1937 2937 3937 4937 5937 6937 7937 8937 9937
8 7 9 | 938 1938 2938 3938 4938 5938 6938 7938 8938 9938
9 7 9 | 939 1939 2939 3939 4939 5939 6939 7939 8939 9939
0 8 9 | -965 -1965 2965 3965 4965 -5965 6965 -7965 8965 9965
1 8 9 | -964 -1964 2964 3964 4964 -5964 6964 -7964 8964 9964
2 8 9 | -963 -1963 2963 3963 4963 -5963 6963 -7963 8963 9963
3 8 9 | 943 1943 2943 3943 4943 5943 6943 7943 8943 9943
4 8 9 | 944 1944 2944 3944 4944 5944 6944 7944 8944 9944
5 8 9 | 945 1945 2945 3945 4945 5945 6945 7945 8945 9945
6 8 9 | 946 1946 2946 3946 4946 5946 6946 7946 8946 9946
7 8 9 | 947 1947 2947 3947 4947 5947 6947 7947 8947 9947
8 8 9 | 948 1948 2948 3948 4948 5948 6948 7948 8948 9948
9 8 9 | 949 1949 2949 3949 4949 5949 6949 7949 8949 9949
0 9 9 | -935 -1935 2935 3935 4935 -5935 6935 -7935 8935 9935
1 9 9 | -934 -1934 2934 3934 4934 -5934 6934 -7934 8934 9934
2 9 9 | -933 -1933 2933 3933 4933 -5933 6933 -7933 8933 9933
3 9 9 | 953 1953 2953 3953 4953 5953 6953 7953 8953 9953
4 9 9 | 954 1954 2954 3954 4954 5954 6954 7954 8954 9954
5 9 9 | 955 1955 2955 3955 4955 5955 6955 7955 8955 9955
6 9 9 | 956 1956 2956 3956 4956 5956 6956 7956 8956 9956
7 9 9 | 957 1957 2957 3957 4957 5957 6957 7957 8957 9957
8 9 9 | 958 1958 2958 3958 4958 5958 6958 7958 8958 9958
9 9 9 | 959 1959 2959 3959 4959 5959 6959 7959 8959 9959
0 0 0 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 0 0 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 0 0 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 0 0 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 0 0 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 0 0 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 0 0 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 0 0 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 0 0 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 0 0 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 1 0 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 1 0 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 1 0 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 1 0 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 1 0 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 1 0 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 1 0 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 1 0 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 1 0 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 1 0 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 2 0 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 2 0 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 2 0 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 2 0 | 633 -1633 -2633 3633 -4633 -5633 6633 7633 8633 9633
4 2 0 | 634 -1634 -2634 3634 -4634 -5634 6634 7634 8634 9634
5 2 0 | 635 -1635 -2635 3635 -4635 -5635 6635 7635 8635 9635
6 2 0 | 636 -1636 -2636 3636 -4636 -5636 6636 7636 8636 9636
7 2 0 | 637 -1637 -2637 3637 -4637 -5637 6637 7637 8637 9637
8 2 0 | 638 -1638 -2638 3638 -4638 -5638 6638 7638 8638 9638
9 2 0 | 639 -1639 -2639 3639 -4639 -5639 6639 7639 8639 9639
0 3 0 | -665 1665 -2665 3665 -4665 5665 6665 -7665 8665 9665
1 3 0 | -664 1664 -2664 3664 -4664 5664 6664 -7664 8664 9664
2 3 0 | -663 1663 -2663 3663 -4663 5663 6663 -7663 8663 9663
3 3 0 | 433 1433 2433 3433 4433 5433 6433 7433 8433 9433
4 3 0 | 434 1434 2434 3434 4434 5434 6434 7434 8434 9434
5 3 0 | 435 1435 2435 3435 4435 5435 6435 7435 8435 9435
6 3 0 | 436 1436 2436 3436 4436 5436 6436 7436 8436 9436
7 3 0 | 437 1437 2437 3437 4437 5437 6437 7437 8437 9437
8 3 0 | 438 1438 2438 3438 4438 5438 6438 7438 8438 9438
9 3 0 | 439 1439 2439 3439 4439 5439 6439 7439 8439 9439
0 4 0 | -655 1655 -2655 3655 -4655 5655 6655 -7655 8655 9655
1 4 0 | -654 1654 -2654 3654 -4654 5654 6654 -7654 8654 9654
2 4 0 | -653 1653 -2653 3653 -4653 5653 6653 -7653 8653 9653
3 4 0 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 4 0 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 4 0 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 4 0 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 4 0 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 4 0 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 4 0 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 5 0 | -645 1645 -2645 3645 -4645 5645 6645 -7645 8645 9645
1 5 0 | -644 1644 -2644 3644 -4644 5644 6644 -7644 8644 9644
2 5 0 | -643 1643 -2643 3643 -4643 5643 6643 -7643 8643 9643
3 5 0 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 5 0 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 5 0 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 5 0 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 5 0 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 5 0 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 5 0 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 6 0 | -635 1635 -2635 3635 -4635 5635 6635 -7635 8635 9635
1 6 0 | -634 1634 -2634 3634 -4634 5634 6634 -7634 8634 9634
2 6 0 | -633 1633 -2633 3633 -4633 5633 6633 -7633 8633 9633
3 6 0 | 463 1463 2463 3463 4463 5463 6463 7463 8463 9463
4 6 0 | 464 1464 2464 3464 4464 5464 6464 7464 8464 9464
5 6 0 | 465 1465 2465 3465 4465 5465 6465 7465 8465 9465
6 6 0 | 466 1466 2466 3466 4466 5466 6466 7466 8466 9466
7 6 0 | 467 1467 2467 3467 4467 5467 6467 7467 8467 9467
8 6 0 | 468 1468 2468 3468 4468 5468 6468 7468 8468 9468
9 6 0 | 469 1469 2469 3469 4469 5469 6469 7469 8469 9469
0 7 0 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 7 0 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 7 0 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 7 0 | 663 -1663 -2663 3663 -4663 -5663 6663 7663 8663 9663
4 7 0 | 664 -1664 -2664 3664 -4664 -5664 6664 7664 8664 9664
5 7 0 | 665 -1665 -2665 3665 -4665 -5665 6665 7665 8665 9665
6 7 0 | 666 -1666 -2666 3666 -4666 -5666 6666 7666 8666 9666
7 7 0 | 667 -1667 -2667 3667 -4667 -5667 6667 7667 8667 9667
8 7 0 | 668 -1668 -2668 3668 -4668 -5668 6668 7668 8668 9668
9 7 0 | 669 -1669 -2669 3669 -4669 -5669 6669 7669 8669 9669
0 8 0 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 8 0 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 8 0 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 8 0 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 8 0 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 8 0 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 8 0 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 8 0 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 8 0 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 8 0 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 9 0 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 9 0 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 9 0 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 9 0 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 9 0 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 9 0 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 9 0 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 9 0 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 9 0 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 9 0 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 0 1 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 0 1 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 0 1 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 0 1 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 0 1 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 0 1 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 0 1 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 0 1 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 0 1 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 0 1 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 1 1 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 1 1 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 1 1 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 1 1 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 1 1 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 1 1 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 1 1 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 1 1 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 1 1 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 1 1 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
0 2 1 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 2 1 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 2 1 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 2 1 | 333 -1333 -2333 3333 -4333 -5333 6333 7333 8333 9333
4 2 1 | 334 -1334 -2334 3334 -4334 -5334 6334 7334 8334 9334
5 2 1 | 335 -1335 -2335 3335 -4335 -5335 6335 7335 8335 9335
6 2 1 | 336 -1336 -2336 3336 -4336 -5336 6336 7336 8336 9336
7 2 1 | 337 -1337 -2337 3337 -4337 -5337 6337 7337 8337 9337
8 2 1 | 338 -1338 -2338 3338 -4338 -5338 6338 7338 8338 9338
9 2 1 | 339 -1339 -2339 3339 -4339 -5339 6339 7339 8339 9339
0 3 1 | -365 1365 -2365 3365 -4365 5365 6365 -7365 8365 9365
1 3 1 | -364 1364 -2364 3364 -4364 5364 6364 -7364 8364 9364
2 3 1 | -363 1363 -2363 3363 -4363 5363 6363 -7363 8363 9363
3 3 1 | 533 1533 2533 3533 4533 5533 6533 7533 8533 9533
4 3 1 | 534 1534 2534 3534 4534 5534 6534 7534 8534 9534
5 3 1 | 535 1535 2535 3535 4535 5535 6535 7535 8535 9535
6 3 1 | 536 1536 2536 3536 4536 5536 6536 7536 8536 9536
7 3 1 | 537 1537 2537 3537 4537 5537 6537 7537 8537 9537
8 3 1 | 538 1538 2538 3538 4538 5538 6538 7538 8538 9538
9 3 1 | 539 1539 2539 3539 4539 5539 6539 7539 8539 9539
0 4 1 | -355 1355 -2355 3355 -4355 5355 6355 -7355 8355 9355
1 4 1 | -354 1354 -2354 3354 -4354 5354 6354 -7354 8354 9354
2 4 1 | -353 1353 -2353 3353 -4353 5353 6353 -7353 8353 9353
3 4 1 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 4 1 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 4 1 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 4 1 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 4 1 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 4 1 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 4 1 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 5 1 | -345 1345 -2345 3345 -4345 5345 6345 -7345 8345 9345
1 5 1 | -344 1344 -2344 3344 -4344 5344 6344 -7344 8344 9344
2 5 1 | -343 1343 -2343 3343 -4343 5343 6343 -7343 8343 9343
3 5 1 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 5 1 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 5 1 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 5 1 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 5 1 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 5 1 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 5 1 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 6 1 | -335 1335 -2335 3335 -4335 5335 6335 -7335 8335 9335
1 6 1 | -334 1334 -2334 3334 -4334 5334 6334 -7334 8334 9334
2 6 1 | -333 1333 -2333 3333 -4333 5333 6333 -7333 8333 9333
3 6 1 | 563 1563 2563 3563 4563 5563 6563 7563 8563 9563
4 6 1 | 564 1564 2564 3564 4564 5564 6564 7564 8564 9564
5 6 1 | 565 1565 2565 3565 4565 5565 6565 7565 8565 9565
6 6 1 | 566 1566 2566 3566 4566 5566 6566 7566 8566 9566
7 6 1 | 567 1567 2567 3567 4567 5567 6567 7567 8567 9567
8 6 1 | 568 1568 2568 3568 4568 5568 6568 7568 8568 9568
9 6 1 | 569 1569 2569 3569 4569 5569 6569 7569 8569 9569
0 7 1 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 7 1 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 7 1 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 7 1 | 363 -1363 -2363 3363 -4363 -5363 6363 7363 8363 9363
4 7 1 | 364 -1364 -2364 3364 -4364 -5364 6364 7364 8364 9364
5 7 1 | 365 -1365 -2365 3365 -4365 -5365 6365 7365 8365 9365
6 7 1 | 366 -1366 -2366 3366 -4366 -5366 6366 7366 8366 9366
7 7 1 | 367 -1367 -2367 3367 -4367 -5367 6367 7367 8367 9367
8 7 1 | 368 -1368 -2368 3368 -4368 -5368 6368 7368 8368 9368
9 7 1 | 369 -1369 -2369 3369 -4369 -5369 6369 7369 8369 9369
0 8 1 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 8 1 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 8 1 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 8 1 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 8 1 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 8 1 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 8 1 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 8 1 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 8 1 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 8 1 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 9 1 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 9 1 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 9 1 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 9 1 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 9 1 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 9 1 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 9 1 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 9 1 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 9 1 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 9 1 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
0 0 2 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 0 2 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 0 2 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 0 2 | 453 -1453 -2453 3453 -4453 -5453 6453 7453 8453 9453
4 0 2 | 454 -1454 -2454 3454 -4454 -5454 6454 7454 8454 9454
5 0 2 | 455 -1455 -2455 3455 -4455 -5455 6455 7455 8455 9455
6 0 2 | 456 -1456 -2456 3456 -4456 -5456 6456 7456 8456 9456
7 0 2 | 457 -1457 -2457 3457 -4457 -5457 6457 7457 8457 9457
8 0 2 | 458 -1458 -2458 3458 -4458 -5458 6458 7458 8458 9458
9 0 2 | 459 -1459 -2459 3459 -4459 -5459 6459 7459 8459 9459
0 1 2 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 1 2 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 1 2 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 1 2 | 443 -1443 -2443 3443 -4443 -5443 6443 7443 8443 9443
4 1 2 | 444 -1444 -2444 3444 -4444 -5444 6444 7444 8444 9444
5 1 2 | 445 -1445 -2445 3445 -4445 -5445 6445 7445 8445 9445
6 1 2 | 446 -1446 -2446 3446 -4446 -5446 6446 7446 8446 9446
7 1 2 | 447 -1447 -2447 3447 -4447 -5447 6447 7447 8447 9447
8 1 2 | 448 -1448 -2448 3448 -4448 -5448 6448 7448 8448 9448
9 1 2 | 449 -1449 -2449 3449 -4449 -5449 6449 7449 8449 9449
0 2 2 | -665 -1665 2665 3665 4665 -5665 6665 -7665 8665 9665
1 2 2 | -664 -1664 2664 3664 4664 -5664 6664 -7664 8664 9664
2 2 2 | -663 -1663 2663 3663 4663 -5663 6663 -7663 8663 9663
3 2 2 | 433 -1433 -2433 3433 -4433 -5433 6433 7433 8433 9433
4 2 2 | 434 -1434 -2434 3434 -4434 -5434 6434 7434 8434 9434
5 2 2 | 435 -1435 -2435 3435 -4435 -5435 6435 7435 8435 9435
6 2 2 | 436 -1436 -2436 3436 -4436 -5436 6436 7436 8436 9436
7 2 2 | 437 -1437 -2437 3437 -4437 -5437 6437 7437 8437 9437
8 2 2 | 438 -1438 -2438 3438 -4438 -5438 6438 7438 8438 9438
9 2 2 | 439 -1439 -2439 3439 -4439 -5439 6439 7439 8439 9439
0 3 2 | -465 1465 -2465 3465 -4465 5465 6465 -7465 8465 9465
1 3 2 | -464 1464 -2464 3464 -4464 5464 6464 -7464 8464 9464
2 3 2 | -463 1463 -2463 3463 -4463 5463 6463 -7463 8463 9463
3 3 2 | 633 1633 2633 3633 4633 5633 6633 7633 8633 9633
4 3 2 | 634 1634 2634 3634 4634 5634 6634 7634 8634 9634
5 3 2 | 635 1635 2635 3635 4635 5635 6635 7635 8635 9635
6 3 2 | 636 1636 2636 3636 4636 5636 6636 7636 8636 9636
7 3 2 | 637 1637 2637 3637 4637 5637 6637 7637 8637 9637
8 3 2 | 638 1638 2638 3638 4638 5638 6638 7638 8638 9638
9 3 2 | 639 1639 2639 3639 4639 5639 6639 7639 8639 9639
0 4 2 | -455 1455 -2455 3455 -4455 5455 6455 -7455 8455 9455
1 4 2 | -454 1454 -2454 3454 -4454 5454 6454 -7454 8454 9454
2 4 2 | -453 1453 -2453 3453 -4453 5453 6453 -7453 8453 9453
3 4 2 | 643 1643 2643 3643 4643 5643 6643 7643 8643 9643
4 4 2 | 644 1644 2644 3644 4644 5644 6644 7644 8644 9644
5 4 2 | 645 1645 2645 3645 4645 5645 6645 7645 8645 9645
6 4 2 | 646 1646 2646 3646 4646 5646 6646 7646 8646 9646
7 4 2 | 647 1647 2647 3647 4647 5647 6647 7647 8647 9647
8 4 2 | 648 1648 2648 3648 4648 5648 6648 7648 8648 9648
9 4 2 | 649 1649 2649 3649 4649 5649 6649 7649 8649 9649
0 5 2 | -445 1445 -2445 3445 -4445 5445 6445 -7445 8445 9445
1 5 2 | -444 1444 -2444 3444 -4444 5444 6444 -7444 8444 9444
2 5 2 | -443 1443 -2443 3443 -4443 5443 6443 -7443 8443 9443
3 5 2 | 653 1653 2653 3653 4653 5653 6653 7653 8653 9653
4 5 2 | 654 1654 2654 3654 4654 5654 6654 7654 8654 9654
5 5 2 | 655 1655 2655 3655 4655 5655 6655 7655 8655 9655
6 5 2 | 656 1656 2656 3656 4656 5656 6656 7656 8656 9656
7 5 2 | 657 1657 2657 3657 4657 5657 6657 7657 8657 9657
8 5 2 | 658 1658 2658 3658 4658 5658 6658 7658 8658 9658
9 5 2 | 659 1659 2659 3659 4659 5659 6659 7659 8659 9659
0 6 2 | -435 1435 -2435 3435 -4435 5435 6435 -7435 8435 9435
1 6 2 | -434 1434 -2434 3434 -4434 5434 6434 -7434 8434 9434
2 6 2 | -433 1433 -2433 3433 -4433 5433 6433 -7433 8433 9433
3 6 2 | 663 1663 2663 3663 4663 5663 6663 7663 8663 9663
4 6 2 | 664 1664 2664 3664 4664 5664 6664 7664 8664 9664
5 6 2 | 665 1665 2665 3665 4665 5665 6665 7665 8665 9665
6 6 2 | 666 1666 2666 3666 4666 5666 6666 7666 8666 9666
7 6 2 | 667 1667 2667 3667 4667 5667 6667 7667 8667 9667
8 6 2 | 668 1668 2668 3668 4668 5668 6668 7668 8668 9668
9 6 2 | 669 1669 2669 3669 4669 5669 6669 7669 8669 9669
0 7 2 | -635 -1635 2635 3635 4635 -5635 6635 -7635 8635 9635
1 7 2 | -634 -1634 2634 3634 4634 -5634 6634 -7634 8634 9634
2 7 2 | -633 -1633 2633 3633 4633 -5633 6633 -7633 8633 9633
3 7 2 | 463 -1463 -2463 3463 -4463 -5463 6463 7463 8463 9463
4 7 2 | 464 -1464 -2464 3464 -4464 -5464 6464 7464 8464 9464
5 7 2 | 465 -1465 -2465 3465 -4465 -5465 6465 7465 8465 9465
6 7 2 | 466 -1466 -2466 3466 -4466 -5466 6466 7466 8466 9466
7 7 2 | 467 -1467 -2467 3467 -4467 -5467 6467 7467 8467 9467
8 7 2 | 468 -1468 -2468 3468 -4468 -5468 6468 7468 8468 9468
9 7 2 | 469 -1469 -2469 3469 -4469 -5469 6469 7469 8469 9469
0 8 2 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 8 2 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 8 2 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 8 2 | 453 -1453 -2453 3453 -4453 -5453 6453 7453 8453 9453
4 8 2 | 454 -1454 -2454 3454 -4454 -5454 6454 7454 8454 9454
5 8 2 | 455 -1455 -2455 3455 -4455 -5455 6455 7455 8455 9455
6 8 2 | 456 -1456 -2456 3456 -4456 -5456 6456 7456 8456 9456
7 8 2 | 457 -1457 -2457 3457 -4457 -5457 6457 7457 8457 9457
8 8 2 | 458 -1458 -2458 3458 -4458 -5458 6458 7458 8458 9458
9 8 2 | 459 -1459 -2459 3459 -4459 -5459 6459 7459 8459 9459
0 9 2 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 9 2 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 9 2 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 9 2 | 443 -1443 -2443 3443 -4443 -5443 6443 7443 8443 9443
4 9 2 | 444 -1444 -2444 3444 -4444 -5444 6444 7444 8444 9444
5 9 2 | 445 -1445 -2445 3445 -4445 -5445 6445 7445 8445 9445
6 9 2 | 446 -1446 -2446 3446 -4446 -5446 6446 7446 8446 9446
7 9 2 | 447 -1447 -2447 3447 -4447 -5447 6447 7447 8447 9447
8 9 2 | 448 -1448 -2448 3448 -4448 -5448 6448 7448 8448 9448
9 9 2 | 449 -1449 -2449 3449 -4449 -5449 6449 7449 8449 9449
0 0 3 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 0 3 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 0 3 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 0 3 | 553 -1553 -2553 3553 -4553 -5553 6553 7553 8553 9553
4 0 3 | 554 -1554 -2554 3554 -4554 -5554 6554 7554 8554 9554
5 0 3 | 555 -1555 -2555 3555 -4555 -5555 6555 7555 8555 9555
6 0 3 | 556 -1556 -2556 3556 -4556 -5556 6556 7556 8556 9556
7 0 3 | 557 -1557 -2557 3557 -4557 -5557 6557 7557 8557 9557
8 0 3 | 558 -1558 -2558 3558 -4558 -5558 6558 7558 8558 9558
9 0 3 | 559 -1559 -2559 3559 -4559 -5559 6559 7559 8559 9559
0 1 3 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 1 3 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 1 3 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 1 3 | 543 -1543 -2543 3543 -4543 -5543 6543 7543 8543 9543
4 1 3 | 544 -1544 -2544 3544 -4544 -5544 6544 7544 8544 9544
5 1 3 | 545 -1545 -2545 3545 -4545 -5545 6545 7545 8545 9545
6 1 3 | 546 -1546 -2546 3546 -4546 -5546 6546 7546 8546 9546
7 1 3 | 547 -1547 -2547 3547 -4547 -5547 6547 7547 8547 9547
8 1 3 | 548 -1548 -2548 3548 -4548 -5548 6548 7548 8548 9548
9 1 3 | 549 -1549 -2549 3549 -4549 -5549 6549 7549 8549 9549
0 2 3 | -365 -1365 2365 3365 4365 -5365 6365 -7365 8365 9365
1 2 3 | -364 -1364 2364 3364 4364 -5364 6364 -7364 8364 9364
2 2 3 | -363 -1363 2363 3363 4363 -5363 6363 -7363 8363 9363
3 2 3 | 533 -1533 -2533 3533 -4533 -5533 6533 7533 8533 9533
4 2 3 | 534 -1534 -2534 3534 -4534 -5534 6534 7534 8534 9534
5 2 3 | 535 -1535 -2535 3535 -4535 -5535 6535 7535 8535 9535
6 2 3 | 536 -1536 -2536 3536 -4536 -5536 6536 7536 8536 9536
7 2 3 | 537 -1537 -2537 3537 -4537 -5537 6537 7537 8537 9537
8 2 3 | 538 -1538 -2538 3538 -4538 -5538 6538 7538 8538 9538
9 2 3 | 539 -1539 -2539 3539 -4539 -5539 6539 7539 8539 9539
0 3 3 | -565 1565 -2565 3565 -4565 5565 6565 -7565 8565 9565
1 3 3 | -564 1564 -2564 3564 -4564 5564 6564 -7564 8564 9564
2 3 3 | -563 1563 -2563 3563 -4563 5563 6563 -7563 8563 9563
3 3 3 | 333 1333 2333 3333 4333 5333 6333 7333 8333 9333
4 3 3 | 334 1334 2334 3334 4334 5334 6334 7334 8334 9334
5 3 3 | 335 1335 2335 3335 4335 5335 6335 7335 8335 9335
6 3 3 | 336 1336 2336 3336 4336 5336 6336 7336 8336 9336
7 3 3 | 337 1337 2337 3337 4337 5337 6337 7337 8337 9337
8 3 3 | 338 1338 2338 3338 4338 5338 6338 7338 8338 9338
9 3 3 | 339 1339 2339 3339 4339 5339 6339 7339 8339 9339
0 4 3 | -555 1555 -2555 3555 -4555 5555 6555 -7555 8555 9555
1 4 3 | -554 1554 -2554 3554 -4554 5554 6554 -7554 8554 9554
2 4 3 | -553 1553 -2553 3553 -4553 5553 6553 -7553 8553 9553
3 4 3 | 343 1343 2343 3343 4343 5343 6343 7343 8343 9343
4 4 3 | 344 1344 2344 3344 4344 5344 6344 7344 8344 9344
5 4 3 | 345 1345 2345 3345 4345 5345 6345 7345 8345 9345
6 4 3 | 346 1346 2346 3346 4346 5346 6346 7346 8346 9346
7 4 3 | 347 1347 2347 3347 4347 5347 6347 7347 8347 9347
8 4 3 | 348 1348 2348 3348 4348 5348 6348 7348 8348 9348
9 4 3 | 349 1349 2349 3349 4349 5349 6349 7349 8349 9349
0 5 3 | -545 1545 -2545 3545 -4545 5545 6545 -7545 8545 9545
1 5 3 | -544 1544 -2544 3544 -4544 5544 6544 -7544 8544 9544
2 5 3 | -543 1543 -2543 3543 -4543 5543 6543 -7543 8543 9543
3 5 3 | 353 1353 2353 3353 4353 5353 6353 7353 8353 9353
4 5 3 | 354 1354 2354 3354 4354 5354 6354 7354 8354 9354
5 5 3 | 355 1355 2355 3355 4355 5355 6355 7355 8355 9355
6 5 3 | 356 1356 2356 3356 4356 5356 6356 7356 8356 9356
7 5 3 | 357 1357 2357 3357 4357 5357 6357 7357 8357 9357
8 5 3 | 358 1358 2358 3358 4358 5358 6358 7358 8358 9358
9 5 3 | 359 1359 2359 3359 4359 5359 6359 7359 8359 9359
0 6 3 | -535 1535 -2535 3535 -4535 5535 6535 -7535 8535 9535
1 6 3 | -534 1534 -2534 3534 -4534 5534 6534 -7534 8534 9534
2 6 3 | -533 1533 -2533 3533 -4533 5533 6533 -7533 8533 9533
3 6 3 | 363 1363 2363 3363 4363 5363 6363 7363 8363 9363
4 6 3 | 364 1364 2364 3364 4364 5364 6364 7364 8364 9364
5 6 3 | 365 1365 2365 3365 4365 5365 6365 7365 8365 9365
6 6 3 | 366 1366 2366 3366 4366 5366 6366 7366 8366 9366
7 6 3 | 367 1367 2367 3367 4367 5367 6367 7367 8367 9367
8 6 3 | 368 1368 2368 3368 4368 5368 6368 7368 8368 9368
9 6 3 | 369 1369 2369 3369 4369 5369 6369 7369 8369 9369
0 7 3 | -335 -1335 2335 3335 4335 -5335 6335 -7335 8335 9335
1 7 3 | -334 -1334 2334 3334 4334 -5334 6334 -7334 8334 9334
2 7 3 | -333 -1333 2333 3333 4333 -5333 6333 -7333 8333 9333
3 7 3 | 563 -1563 -2563 3563 -4563 -5563 6563 7563 8563 9563
4 7 3 | 564 -1564 -2564 3564 -4564 -5564 6564 7564 8564 9564
5 7 3 | 565 -1565 -2565 3565 -4565 -5565 6565 7565 8565 9565
6 7 3 | 566 -1566 -2566 3566 -4566 -5566 6566 7566 8566 9566
7 7 3 | 567 -1567 -2567 3567 -4567 -5567 6567 7567 8567 9567
8 7 3 | 568 -1568 -2568 3568 -4568 -5568 6568 7568 8568 9568
9 7 3 | 569 -1569 -2569 3569 -4569 -5569 6569 7569 8569 9569
0 8 3 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 8 3 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 8 3 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 8 3 | 553 -1553 -2553 3553 -4553 -5553 6553 7553 8553 9553
4 8 3 | 554 -1554 -2554 3554 -4554 -5554 6554 7554 8554 9554
5 8 3 | 555 -1555 -2555 3555 -4555 -5555 6555 7555 8555 9555
6 8 3 | 556 -1556 -2556 3556 -4556 -5556 6556 7556 8556 9556
7 8 3 | 557 -1557 -2557 3557 -4557 -5557 6557 7557 8557 9557
8 8 3 | 558 -1558 -2558 3558 -4558 -5558 6558 7558 8558 9558
9 8 3 | 559 -1559 -2559 3559 -4559 -5559 6559 7559 8559 9559
0 9 3 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 9 3 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 9 3 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 9 3 | 543 -1543 -2543 3543 -4543 -5543 6543 7543 8543 9543
4 9 3 | 544 -1544 -2544 3544 -4544 -5544 6544 7544 8544 9544
5 9 3 | 545 -1545 -2545 3545 -4545 -5545 6545 7545 8545 9545
6 9 3 | 546 -1546 -2546 3546 -4546 -5546 6546 7546 8546 9546
7 9 3 | 547 -1547 -2547 3547 -4547 -5547 6547 7547 8547 9547
8 9 3 | 548 -1548 -2548 3548 -4548 -5548 6548 7548 8548 9548
9 9 3 | 549 -1549 -2549 3549 -4549 -5549 6549 7549 8549 9549
0 0 4 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 0 4 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 0 4 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 0 4 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 0 4 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 0 4 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 0 4 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 0 4 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 0 4 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 0 4 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 1 4 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 1 4 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 1 4 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 1 4 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 1 4 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 1 4 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 1 4 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 1 4 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 1 4 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 1 4 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 2 4 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 2 4 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 2 4 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 2 4 | 633 -1633 -2633 3633 -4633 -5633 6633 7633 8633 9633
4 2 4 | 634 -1634 -2634 3634 -4634 -5634 6634 7634 8634 9634
5 2 4 | 635 -1635 -2635 3635 -4635 -5635 6635 7635 8635 9635
6 2 4 | 636 -1636 -2636 3636 -4636 -5636 6636 7636 8636 9636
7 2 4 | 637 -1637 -2637 3637 -4637 -5637 6637 7637 8637 9637
8 2 4 | 638 -1638 -2638 3638 -4638 -5638 6638 7638 8638 9638
9 2 4 | 639 -1639 -2639 3639 -4639 -5639 6639 7639 8639 9639
0 3 4 | -665 1665 -2665 3665 -4665 5665 6665 -7665 8665 9665
1 3 4 | -664 1664 -2664 3664 -4664 5664 6664 -7664 8664 9664
2 3 4 | -663 1663 -2663 3663 -4663 5663 6663 -7663 8663 9663
3 3 4 | 433 1433 2433 3433 4433 5433 6433 7433 8433 9433
4 3 4 | 434 1434 2434 3434 4434 5434 6434 7434 8434 9434
5 3 4 | 435 1435 2435 3435 4435 5435 6435 7435 8435 9435
6 3 4 | 436 1436 2436 3436 4436 5436 6436 7436 8436 9436
7 3 4 | 437 1437 2437 3437 4437 5437 6437 7437 8437 9437
8 3 4 | 438 1438 2438 3438 4438 5438 6438 7438 8438 9438
9 3 4 | 439 1439 2439 3439 4439 5439 6439 7439 8439 9439
0 4 4 | -655 1655 -2655 3655 -4655 5655 6655 -7655 8655 9655
1 4 4 | -654 1654 -2654 3654 -4654 5654 6654 -7654 8654 9654
2 4 4 | -653 1653 -2653 3653 -4653 5653 6653 -7653 8653 9653
3 4 4 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 4 4 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 4 4 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 4 4 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 4 4 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 4 4 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 4 4 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 5 4 | -645 1645 -2645 3645 -4645 5645 6645 -7645 8645 9645
1 5 4 | -644 1644 -2644 3644 -4644 5644 6644 -7644 8644 9644
2 5 4 | -643 1643 -2643 3643 -4643 5643 6643 -7643 8643 9643
3 5 4 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 5 4 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 5 4 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 5 4 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 5 4 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 5 4 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 5 4 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 6 4 | -635 1635 -2635 3635 -4635 5635 6635 -7635 8635 9635
1 6 4 | -634 1634 -2634 3634 -4634 5634 6634 -7634 8634 9634
2 6 4 | -633 1633 -2633 3633 -4633 5633 6633 -7633 8633 9633
3 6 4 | 463 1463 2463 3463 4463 5463 6463 7463 8463 9463
4 6 4 | 464 1464 2464 3464 4464 5464 6464 7464 8464 9464
5 6 4 | 465 1465 2465 3465 4465 5465 6465 7465 8465 9465
6 6 4 | 466 1466 2466 3466 4466 5466 6466 7466 8466 9466
7 6 4 | 467 1467 2467 3467 4467 5467 6467 7467 8467 9467
8 6 4 | 468 1468 2468 3468 4468 5468 6468 7468 8468 9468
9 6 4 | 469 1469 2469 3469 4469 5469 6469 7469 8469 9469
0 7 4 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 7 4 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 7 4 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 7 4 | 663 -1663 -2663 3663 -4663 -5663 6663 7663 8663 9663
4 7 4 | 664 -1664 -2664 3664 -4664 -5664 6664 7664 8664 9664
5 7 4 | 665 -1665 -2665 3665 -4665 -5665 6665 7665 8665 9665
6 7 4 | 666 -1666 -2666 3666 -4666 -5666 6666 7666 8666 9666
7 7 4 | 667 -1667 -2667 3667 -4667 -5667 6667 7667 8667 9667
8 7 4 | 668 -1668 -2668 3668 -4668 -5668 6668 7668 8668 9668
9 7 4 | 669 -1669 -2669 3669 -4669 -5669 6669 7669 8669 9669
0 8 4 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 8 4 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 8 4 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 8 4 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 8 4 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 8 4 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 8 4 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 8 4 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 8 4 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 8 4 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 9 4 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 9 4 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 9 4 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 9 4 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 9 4 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 9 4 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 9 4 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 9 4 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 9 4 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 9 4 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 0 5 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 0 5 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 0 5 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 0 5 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 0 5 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 0 5 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 0 5 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 0 5 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 0 5 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 0 5 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 1 5 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 1 5 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 1 5 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 1 5 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 1 5 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 1 5 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 1 5 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 1 5 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 1 5 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 1 5 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
0 2 5 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 2 5 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 2 5 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 2 5 | 333 -1333 -2333 3333 -4333 -5333 6333 7333 8333 9333
4 2 5 | 334 -1334 -2334 3334 -4334 -5334 6334 7334 8334 9334
5 2 5 | 335 -1335 -2335 3335 -4335 -5335 6335 7335 8335 9335
6 2 5 | 336 -1336 -2336 3336 -4336 -5336 6336 7336 8336 9336
7 2 5 | 337 -1337 -2337 3337 -4337 -5337 6337 7337 8337 9337
8 2 5 | 338 -1338 -2338 3338 -4338 -5338 6338 7338 8338 9338
9 2 5 | 339 -1339 -2339 3339 -4339 -5339 6339 7339 8339 9339
0 3 5 | -365 1365 -2365 3365 -4365 5365 6365 -7365 8365 9365
1 3 5 | -364 1364 -2364 3364 -4364 5364 6364 -7364 8364 9364
2 3 5 | -363 1363 -2363 3363 -4363 5363 6363 -7363 8363 9363
3 3 5 | 533 1533 2533 3533 4533 5533 6533 7533 8533 9533
4 3 5 | 534 1534 2534 3534 4534 5534 6534 7534 8534 9534
5 3 5 | 535 1535 2535 3535 4535 5535 6535 7535 8535 9535
6 3 5 | 536 1536 2536 3536 4536 5536 6536 7536 8536 9536
7 3 5 | 537 1537 2537 3537 4537 5537 6537 7537 8537 9537
8 3 5 | 538 1538 2538 3538 4538 5538 6538 7538 8538 9538
9 3 5 | 539 1539 2539 3539 4539 5539 6539 7539 8539 9539
0 4 5 | -355 1355 -2355 3355 -4355 5355 6355 -7355 8355 9355
1 4 5 | -354 1354 -2354 3354 -4354 5354 6354 -7354 8354 9354
2 4 5 | -353 1353 -2353 3353 -4353 5353 6353 -7353 8353 9353
3 4 5 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 4 5 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 4 5 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 4 5 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 4 5 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 4 5 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 4 5 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 5 5 | -345 1345 -2345 3345 -4345 5345 6345 -7345 8345 9345
1 5 5 | -344 1344 -2344 3344 -4344 5344 6344 -7344 8344 9344
2 5 5 | -343 1343 -2343 3343 -4343 5343 6343 -7343 8343 9343
3 5 5 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 5 5 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 5 5 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 5 5 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 5 5 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 5 5 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 5 5 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 6 5 | -335 1335 -2335 3335 -4335 5335 6335 -7335 8335 9335
1 6 5 | -334 1334 -2334 3334 -4334 5334 6334 -7334 8334 9334
2 6 5 | -333 1333 -2333 3333 -4333 5333 6333 -7333 8333 9333
3 6 5 | 563 1563 2563 3563 4563 5563 6563 7563 8563 9563
4 6 5 | 564 1564 2564 3564 4564 5564 6564 7564 8564 9564
5 6 5 | 565 1565 2565 3565 4565 5565 6565 7565 8565 9565
6 6 5 | 566 1566 2566 3566 4566 5566 6566 7566 8566 9566
7 6 5 | 567 1567 2567 3567 4567 5567 6567 7567 8567 9567
8 6 5 | 568 1568 2568 3568 4568 5568 6568 7568 8568 9568
9 6 5 | 569 1569 2569 3569 4569 5569 6569 7569 8569 9569
0 7 5 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 7 5 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 7 5 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 7 5 | 363 -1363 -2363 3363 -4363 -5363 6363 7363 8363 9363
4 7 5 | 364 -1364 -2364 3364 -4364 -5364 6364 7364 8364 9364
5 7 5 | 365 -1365 -2365 3365 -4365 -5365 6365 7365 8365 9365
6 7 5 | 366 -1366 -2366 3366 -4366 -5366 6366 7366 8366 9366
7 7 5 | 367 -1367 -2367 3367 -4367 -5367 6367 7367 8367 9367
8 7 5 | 368 -1368 -2368 3368 -4368 -5368 6368 7368 8368 9368
9 7 5 | 369 -1369 -2369 3369 -4369 -5369 6369 7369 8369 9369
0 8 5 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 8 5 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 8 5 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 8 5 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 8 5 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 8 5 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 8 5 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 8 5 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 8 5 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 8 5 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 9 5 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 9 5 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 9 5 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 9 5 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 9 5 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 9 5 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 9 5 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 9 5 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 9 5 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 9 5 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
0 0 6 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 0 6 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 0 6 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 0 6 | 453 -1453 -2453 3453 -4453 -5453 6453 7453 8453 9453
4 0 6 | 454 -1454 -2454 3454 -4454 -5454 6454 7454 8454 9454
5 0 6 | 455 -1455 -2455 3455 -4455 -5455 6455 7455 8455 9455
6 0 6 | 456 -1456 -2456 3456 -4456 -5456 6456 7456 8456 9456
7 0 6 | 457 -1457 -2457 3457 -4457 -5457 6457 7457 8457 9457
8 0 6 | 458 -1458 -2458 3458 -4458 -5458 6458 7458 8458 9458
9 0 6 | 459 -1459 -2459 3459 -4459 -5459 6459 7459 8459 9459
0 1 6 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 1 6 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 1 6 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 1 6 | 443 -1443 -2443 3443 -4443 -5443 6443 7443 8443 9443
4 1 6 | 444 -1444 -2444 3444 -4444 -5444 6444 7444 8444 9444
5 1 6 | 445 -1445 -2445 3445 -4445 -5445 6445 7445 8445 9445
6 1 6 | 446 -1446 -2446 3446 -4446 -5446 6446 7446 8446 9446
7 1 6 | 447 -1447 -2447 3447 -4447 -5447 6447 7447 8447 9447
8 1 6 | 448 -1448 -2448 3448 -4448 -5448 6448 7448 8448 9448
9 1 6 | 449 -1449 -2449 3449 -4449 -5449 6449 7449 8449 9449
0 2 6 | -665 -1665 2665 3665 4665 -5665 6665 -7665 8665 9665
1 2 6 | -664 -1664 2664 3664 4664 -5664 6664 -7664 8664 9664
2 2 6 | -663 -1663 2663 3663 4663 -5663 6663 -7663 8663 9663
3 2 6 | 433 -1433 -2433 3433 -4433 -5433 6433 7433 8433 9433
4 2 6 | 434 -1434 -2434 3434 -4434 -5434 6434 7434 8434 9434
5 2 6 | 435 -1435 -2435 3435 -4435 -5435 6435 7435 8435 9435
6 2 6 | 436 -1436 -2436 3436 -4436 -5436 6436 7436 8436 9436
7 2 6 | 437 -1437 -2437 3437 -4437 -5437 6437 7437 8437 9437
8 2 6 | 438 -1438 -2438 3438 -4438 -5438 6438 7438 8438 9438
9 2 6 | 439 -1439 -2439 3439 -4439 -5439 6439 7439 8439 9439
0 3 6 | -465 1465 -2465 3465 -4465 5465 6465 -7465 8465 9465
1 3 6 | -464 1464 -2464 3464 -4464 5464 6464 -7464 8464 9464
2 3 6 | -463 1463 -2463 3463 -4463 5463 6463 -7463 8463 9463
3 3 6 | 633 1633 2633 3633 4633 5633 6633 7633 8633 9633
4 3 6 | 634 1634 2634 3634 4634 5634 6634 7634 8634 9634
5 3 6 | 635 1635 2635 3635 4635 5635 6635 7635 8635 9635
6 3 6 | 636 1636 2636 3636 4636 5636 6636 7636 8636 9636
7 3 6 | 637 1637 2637 3637 4637 5637 6637 7637 8637 9637
8 3 6 | 638 1638 2638 3638 4638 5638 6638 7638 8638 9638
9 3 6 | 639 1639 2639 3639 4639 5639 6639 7639 8639 9639
0 4 6 | -455 1455 -2455 3455 -4455 5455 6455 -7455 8455 9455
1 4 6 | -454 1454 -2454 3454 -4454 5454 6454 -7454 8454 9454
2 4 6 | -453 1453 -2453 3453 -4453 5453 6453 -7453 8453 9453
3 4 6 | 643 1643 2643 3643 4643 5643 6643 7643 8643 9643
4 4 6 | 644 1644 2644 3644 4644 5644 6644 7644 8644 9644
5 4 6 | 645 1645 2645 3645 4645 5645 6645 7645 8645 9645
6 4 6 | 646 1646 2646 3646 4646 5646 6646 7646 8646 9646
7 4 6 | 647 1647 2647 3647 4647 5647 6647 7647 8647 9647
8 4 6 | 648 1648 2648 3648 4648 5648 6648 7648 8648 9648
9 4 6 | 649 1649 2649 3649 4649 5649 6649 7649 8649 9649
0 5 6 | -445 1445 -2445 3445 -4445 5445 6445 -7445 8445 9445
1 5 6 | -444 1444 -2444 3444 -4444 5444 6444 -7444 8444 9444
2 5 6 | -443 1443 -2443 3443 -4443 5443 6443 -7443 8443 9443
3 5 6 | 653 1653 2653 3653 4653 5653 6653 7653 8653 9653
4 5 6 | 654 1654 2654 3654 4654 5654 6654 7654 8654 9654
5 5 6 | 655 1655 2655 3655 4655 5655 6655 7655 8655 9655
6 5 6 | 656 1656 2656 3656 4656 5656 6656 7656 8656 9656
7 5 6 | 657 1657 2657 3657 4657 5657 6657 7657 8657 9657
8 5 6 | 658 1658 2658 3658 4658 5658 6658 7658 8658 9658
9 5 6 | 659 1659 2659 3659 4659 5659 6659 7659 8659 9659
0 6 6 | -435 1435 -2435 3435 -4435 5435 6435 -7435 8435 9435
1 6 6 | -434 1434 -2434 3434 -4434 5434 6434 -7434 8434 9434
2 6 6 | -433 1433 -2433 3433 -4433 5433 6433 -7433 8433 9433
3 6 6 | 663 1663 2663 3663 4663 5663 6663 7663 8663 9663
4 6 6 | 664 1664 2664 3664 4664 5664 6664 7664 8664 9664
5 6 6 | 665 1665 2665 3665 4665 5665 6665 7665 8665 9665
6 6 6 | 666 1666 2666 3666 4666 5666 6666 7666 8666 9666
7 6 6 | 667 1667 2667 3667 4667 5667 6667 7667 8667 9667
8 6 6 | 668 1668 2668 3668 4668 5668 6668 7668 8668 9668
9 6 6 | 669 1669 2669 3669 4669 5669 6669 7669 8669 9669
0 7 6 | -635 -1635 2635 3635 4635 -5635 6635 -7635 8635 9635
1 7 6 | -634 -1634 2634 3634 4634 -5634 6634 -7634 8634 9634
2 7 6 | -633 -1633 2633 3633 4633 -5633 6633 -7633 8633 9633
3 7 6 | 463 -1463 -2463 3463 -4463 -5463 6463 7463 8463 9463
4 7 6 | 464 -1464 -2464 3464 -4464 -5464 6464 7464 8464 9464
5 7 6 | 465 -1465 -2465 3465 -4465 -5465 6465 7465 8465 9465
6 7 6 | 466 -1466 -2466 3466 -4466 -5466 6466 7466 8466 9466
7 7 6 | 467 -1467 -2467 3467 -4467 -5467 6467 7467 8467 9467
8 7 6 | 468 -1468 -2468 3468 -4468 -5468 6468 7468 8468 9468
9 7 6 | 469 -1469 -2469 3469 -4469 -5469 6469 7469 8469 9469
0 8 6 | -645 -1645 2645 3645 4645 -5645 6645 -7645 8645 9645
1 8 6 | -644 -1644 2644 3644 4644 -5644 6644 -7644 8644 9644
2 8 6 | -643 -1643 2643 3643 4643 -5643 6643 -7643 8643 9643
3 8 6 | 453 -1453 -2453 3453 -4453 -5453 6453 7453 8453 9453
4 8 6 | 454 -1454 -2454 3454 -4454 -5454 6454 7454 8454 9454
5 8 6 | 455 -1455 -2455 3455 -4455 -5455 6455 7455 8455 9455
6 8 6 | 456 -1456 -2456 3456 -4456 -5456 6456 7456 8456 9456
7 8 6 | 457 -1457 -2457 3457 -4457 -5457 6457 7457 8457 9457
8 8 6 | 458 -1458 -2458 3458 -4458 -5458 6458 7458 8458 9458
9 8 6 | 459 -1459 -2459 3459 -4459 -5459 6459 7459 8459 9459
0 9 6 | -655 -1655 2655 3655 4655 -5655 6655 -7655 8655 9655
1 9 6 | -654 -1654 2654 3654 4654 -5654 6654 -7654 8654 9654
2 9 6 | -653 -1653 2653 3653 4653 -5653 6653 -7653 8653 9653
3 9 6 | 443 -1443 -2443 3443 -4443 -5443 6443 7443 8443 9443
4 9 6 | 444 -1444 -2444 3444 -4444 -5444 6444 7444 8444 9444
5 9 6 | 445 -1445 -2445 3445 -4445 -5445 6445 7445 8445 9445
6 9 6 | 446 -1446 -2446 3446 -4446 -5446 6446 7446 8446 9446
7 9 6 | 447 -1447 -2447 3447 -4447 -5447 6447 7447 8447 9447
8 9 6 | 448 -1448 -2448 3448 -4448 -5448 6448 7448 8448 9448
9 9 6 | 449 -1449 -2449 3449 -4449 -5449 6449 7449 8449 9449
0 0 7 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 0 7 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 0 7 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 0 7 | 553 -1553 -2553 3553 -4553 -5553 6553 7553 8553 9553
4 0 7 | 554 -1554 -2554 3554 -4554 -5554 6554 7554 8554 9554
5 0 7 | 555 -1555 -2555 3555 -4555 -5555 6555 7555 8555 9555
6 0 7 | 556 -1556 -2556 3556 -4556 -5556 6556 7556 8556 9556
7 0 7 | 557 -1557 -2557 3557 -4557 -5557 6557 7557 8557 9557
8 0 7 | 558 -1558 -2558 3558 -4558 -5558 6558 7558 8558 9558
9 0 7 | 559 -1559 -2559 3559 -4559 -5559 6559 7559 8559 9559
0 1 7 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 1 7 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 1 7 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 1 7 | 543 -1543 -2543 3543 -4543 -5543 6543 7543 8543 9543
4 1 7 | 544 -1544 -2544 3544 -4544 -5544 6544 7544 8544 9544
5 1 7 | 545 -1545 -2545 3545 -4545 -5545 6545 7545 8545 9545
6 1 7 | 546 -1546 -2546 3546 -4546 -5546 6546 7546 8546 9546
7 1 7 | 547 -1547 -2547 3547 -4547 -5547 6547 7547 8547 9547
8 1 7 | 548 -1548 -2548 3548 -4548 -5548 6548 7548 8548 9548
9 1 7 | 549 -1549 -2549 3549 -4549 -5549 6549 7549 8549 9549
0 2 7 | -365 -1365 2365 3365 4365 -5365 6365 -7365 8365 9365
1 2 7 | -364 -1364 2364 3364 4364 -5364 6364 -7364 8364 9364
2 2 7 | -363 -1363 2363 3363 4363 -5363 6363 -7363 8363 9363
3 2 7 | 533 -1533 -2533 3533 -4533 -5533 6533 7533 8533 9533
4 2 7 | 534 -1534 -2534 3534 -4534 -5534 6534 7534 8534 9534
5 2 7 | 535 -1535 -2535 3535 -4535 -5535 6535 7535 8535 9535
6 2 7 | 536 -1536 -2536 3536 -4536 -5536 6536 7536 8536 9536
7 2 7 | 537 -1537 -2537 3537 -4537 -5537 6537 7537 8537 9537
8 2 7 | 538 -1538 -2538 3538 -4538 -5538 6538 7538 8538 9538
9 2 7 | 539 -1539 -2539 3539 -4539 -5539 6539 7539 8539 9539
0 3 7 | -565 1565 -2565 3565 -4565 5565 6565 -7565 8565 9565
1 3 7 | -564 1564 -2564 3564 -4564 5564 6564 -7564 8564 9564
2 3 7 | -563 1563 -2563 3563 -4563 5563 6563 -7563 8563 9563
3 3 7 | 333 1333 2333 3333 4333 5333 6333 7333 8333 9333
4 3 7 | 334 1334 2334 3334 4334 5334 6334 7334 8334 9334
5 3 7 | 335 1335 2335 3335 4335 5335 6335 7335 8335 9335
6 3 7 | 336 1336 2336 3336 4336 5336 6336 7336 8336 9336
7 3 7 | 337 1337 2337 3337 4337 5337 6337 7337 8337 9337
8 3 7 | 338 1338 2338 3338 4338 5338 6338 7338 8338 9338
9 3 7 | 339 1339 2339 3339 4339 5339 6339 7339 8339 9339
0 4 7 | -555 1555 -2555 3555 -4555 5555 6555 -7555 8555 9555
1 4 7 | -554 1554 -2554 3554 -4554 5554 6554 -7554 8554 9554
2 4 7 | -553 1553 -2553 3553 -4553 5553 6553 -7553 8553 9553
3 4 7 | 343 1343 2343 3343 4343 5343 6343 7343 8343 9343
4 4 7 | 344 1344 2344 3344 4344 5344 6344 7344 8344 9344
5 4 7 | 345 1345 2345 3345 4345 5345 6345 7345 8345 9345
6 4 7 | 346 1346 2346 3346 4346 5346 6346 7346 8346 9346
7 4 7 | 347 1347 2347 3347 4347 5347 6347 7347 8347 9347
8 4 7 | 348 1348 2348 3348 4348 5348 6348 7348 8348 9348
9 4 7 | 349 1349 2349 3349 4349 5349 6349 7349 8349 9349
0 5 7 | -545 1545 -2545 3545 -4545 5545 6545 -7545 8545 9545
1 5 7 | -544 1544 -2544 3544 -4544 5544 6544 -7544 8544 9544
2 5 7 | -543 1543 -2543 3543 -4543 5543 6543 -7543 8543 9543
3 5 7 | 353 1353 2353 3353 4353 5353 6353 7353 8353 9353
4 5 7 | 354 1354 2354 3354 4354 5354 6354 7354 8354 9354
5 5 7 | 355 1355 2355 3355 4355 5355 6355 7355 8355 9355
6 5 7 | 356 1356 2356 3356 4356 5356 6356 7356 8356 9356
7 5 7 | 357 1357 2357 3357 4357 5357 6357 7357 8357 9357
8 5 7 | 358 1358 2358 3358 4358 5358 6358 7358 8358 9358
9 5 7 | 359 1359 2359 3359 4359 5359 6359 7359 8359 9359
0 6 7 | -535 1535 -2535 3535 -4535 5535 6535 -7535 8535 9535
1 6 7 | -534 1534 -2534 3534 -4534 5534 6534 -7534 8534 9534
2 6 7 | -533 1533 -2533 3533 -4533 5533 6533 -7533 8533 9533
3 6 7 | 363 1363 2363 3363 4363 5363 6363 7363 8363 9363
4 6 7 | 364 1364 2364 3364 4364 5364 6364 7364 8364 9364
5 6 7 | 365 1365 2365 3365 4365 5365 6365 7365 8365 9365
6 6 7 | 366 1366 2366 3366 4366 5366 6366 7366 8366 9366
7 6 7 | 367 1367 2367 3367 4367 5367 6367 7367 8367 9367
8 6 7 | 368 1368 2368 3368 4368 5368 6368 7368 8368 9368
9 6 7 | 369 1369 2369 3369 4369 5369 6369 7369 8369 9369
0 7 7 | -335 -1335 2335 3335 4335 -5335 6335 -7335 8335 9335
1 7 7 | -334 -1334 2334 3334 4334 -5334 6334 -7334 8334 9334
2 7 7 | -333 -1333 2333 3333 4333 -5333 6333 -7333 8333 9333
3 7 7 | 563 -1563 -2563 3563 -4563 -5563 6563 7563 8563 9563
4 7 7 | 564 -1564 -2564 3564 -4564 -5564 6564 7564 8564 9564
5 7 7 | 565 -1565 -2565 3565 -4565 -5565 6565 7565 8565 9565
6 7 7 | 566 -1566 -2566 3566 -4566 -5566 6566 7566 8566 9566
7 7 7 | 567 -1567 -2567 3567 -4567 -5567 6567 7567 8567 9567
8 7 7 | 568 -1568 -2568 3568 -4568 -5568 6568 7568 8568 9568
9 7 7 | 569 -1569 -2569 3569 -4569 -5569 6569 7569 8569 9569
0 8 7 | -345 -1345 2345 3345 4345 -5345 6345 -7345 8345 9345
1 8 7 | -344 -1344 2344 3344 4344 -5344 6344 -7344 8344 9344
2 8 7 | -343 -1343 2343 3343 4343 -5343 6343 -7343 8343 9343
3 8 7 | 553 -1553 -2553 3553 -4553 -5553 6553 7553 8553 9553
4 8 7 | 554 -1554 -2554 3554 -4554 -5554 6554 7554 8554 9554
5 8 7 | 555 -1555 -2555 3555 -4555 -5555 6555 7555 8555 9555
6 8 7 | 556 -1556 -2556 3556 -4556 -5556 6556 7556 8556 9556
7 8 7 | 557 -1557 -2557 3557 -4557 -5557 6557 7557 8557 9557
8 8 7 | 558 -1558 -2558 3558 -4558 -5558 6558 7558 8558 9558
9 8 7 | 559 -1559 -2559 3559 -4559 -5559 6559 7559 8559 9559
0 9 7 | -355 -1355 2355 3355 4355 -5355 6355 -7355 8355 9355
1 9 7 | -354 -1354 2354 3354 4354 -5354 6354 -7354 8354 9354
2 9 7 | -353 -1353 2353 3353 4353 -5353 6353 -7353 8353 9353
3 9 7 | 543 -1543 -2543 3543 -4543 -5543 6543 7543 8543 9543
4 9 7 | 544 -1544 -2544 3544 -4544 -5544 6544 7544 8544 9544
5 9 7 | 545 -1545 -2545 3545 -4545 -5545 6545 7545 8545 9545
6 9 7 | 546 -1546 -2546 3546 -4546 -5546 6546 7546 8546 9546
7 9 7 | 547 -1547 -2547 3547 -4547 -5547 6547 7547 8547 9547
8 9 7 | 548 -1548 -2548 3548 -4548 -5548 6548 7548 8548 9548
9 9 7 | 549 -1549 -2549 3549 -4549 -5549 6549 7549 8549 9549
0 0 8 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 0 8 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 0 8 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 0 8 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 0 8 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 0 8 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 0 8 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 0 8 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 0 8 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 0 8 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 1 8 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 1 8 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 1 8 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 1 8 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 1 8 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 1 8 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 1 8 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 1 8 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 1 8 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 1 8 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 2 8 | -465 -1465 2465 3465 4465 -5465 6465 -7465 8465 9465
1 2 8 | -464 -1464 2464 3464 4464 -5464 6464 -7464 8464 9464
2 2 8 | -463 -1463 2463 3463 4463 -5463 6463 -7463 8463 9463
3 2 8 | 633 -1633 -2633 3633 -4633 -5633 6633 7633 8633 9633
4 2 8 | 634 -1634 -2634 3634 -4634 -5634 6634 7634 8634 9634
5 2 8 | 635 -1635 -2635 3635 -4635 -5635 6635 7635 8635 9635
6 2 8 | 636 -1636 -2636 3636 -4636 -5636 6636 7636 8636 9636
7 2 8 | 637 -1637 -2637 3637 -4637 -5637 6637 7637 8637 9637
8 2 8 | 638 -1638 -2638 3638 -4638 -5638 6638 7638 8638 9638
9 2 8 | 639 -1639 -2639 3639 -4639 -5639 6639 7639 8639 9639
0 3 8 | -665 1665 -2665 3665 -4665 5665 6665 -7665 8665 9665
1 3 8 | -664 1664 -2664 3664 -4664 5664 6664 -7664 8664 9664
2 3 8 | -663 1663 -2663 3663 -4663 5663 6663 -7663 8663 9663
3 3 8 | 433 1433 2433 3433 4433 5433 6433 7433 8433 9433
4 3 8 | 434 1434 2434 3434 4434 5434 6434 7434 8434 9434
5 3 8 | 435 1435 2435 3435 4435 5435 6435 7435 8435 9435
6 3 8 | 436 1436 2436 3436 4436 5436 6436 7436 8436 9436
7 3 8 | 437 1437 2437 3437 4437 5437 6437 7437 8437 9437
8 3 8 | 438 1438 2438 3438 4438 5438 6438 7438 8438 9438
9 3 8 | 439 1439 2439 3439 4439 5439 6439 7439 8439 9439
0 4 8 | -655 1655 -2655 3655 -4655 5655 6655 -7655 8655 9655
1 4 8 | -654 1654 -2654 3654 -4654 5654 6654 -7654 8654 9654
2 4 8 | -653 1653 -2653 3653 -4653 5653 6653 -7653 8653 9653
3 4 8 | 443 1443 2443 3443 4443 5443 6443 7443 8443 9443
4 4 8 | 444 1444 2444 3444 4444 5444 6444 7444 8444 9444
5 4 8 | 445 1445 2445 3445 4445 5445 6445 7445 8445 9445
6 4 8 | 446 1446 2446 3446 4446 5446 6446 7446 8446 9446
7 4 8 | 447 1447 2447 3447 4447 5447 6447 7447 8447 9447
8 4 8 | 448 1448 2448 3448 4448 5448 6448 7448 8448 9448
9 4 8 | 449 1449 2449 3449 4449 5449 6449 7449 8449 9449
0 5 8 | -645 1645 -2645 3645 -4645 5645 6645 -7645 8645 9645
1 5 8 | -644 1644 -2644 3644 -4644 5644 6644 -7644 8644 9644
2 5 8 | -643 1643 -2643 3643 -4643 5643 6643 -7643 8643 9643
3 5 8 | 453 1453 2453 3453 4453 5453 6453 7453 8453 9453
4 5 8 | 454 1454 2454 3454 4454 5454 6454 7454 8454 9454
5 5 8 | 455 1455 2455 3455 4455 5455 6455 7455 8455 9455
6 5 8 | 456 1456 2456 3456 4456 5456 6456 7456 8456 9456
7 5 8 | 457 1457 2457 3457 4457 5457 6457 7457 8457 9457
8 5 8 | 458 1458 2458 3458 4458 5458 6458 7458 8458 9458
9 5 8 | 459 1459 2459 3459 4459 5459 6459 7459 8459 9459
0 6 8 | -635 1635 -2635 3635 -4635 5635 6635 -7635 8635 9635
1 6 8 | -634 1634 -2634 3634 -4634 5634 6634 -7634 8634 9634
2 6 8 | -633 1633 -2633 3633 -4633 5633 6633 -7633 8633 9633
3 6 8 | 463 1463 2463 3463 4463 5463 6463 7463 8463 9463
4 6 8 | 464 1464 2464 3464 4464 5464 6464 7464 8464 9464
5 6 8 | 465 1465 2465 3465 4465 5465 6465 7465 8465 9465
6 6 8 | 466 1466 2466 3466 4466 5466 6466 7466 8466 9466
7 6 8 | 467 1467 2467 3467 4467 5467 6467 7467 8467 9467
8 6 8 | 468 1468 2468 3468 4468 5468 6468 7468 8468 9468
9 6 8 | 469 1469 2469 3469 4469 5469 6469 7469 8469 9469
0 7 8 | -435 -1435 2435 3435 4435 -5435 6435 -7435 8435 9435
1 7 8 | -434 -1434 2434 3434 4434 -5434 6434 -7434 8434 9434
2 7 8 | -433 -1433 2433 3433 4433 -5433 6433 -7433 8433 9433
3 7 8 | 663 -1663 -2663 3663 -4663 -5663 6663 7663 8663 9663
4 7 8 | 664 -1664 -2664 3664 -4664 -5664 6664 7664 8664 9664
5 7 8 | 665 -1665 -2665 3665 -4665 -5665 6665 7665 8665 9665
6 7 8 | 666 -1666 -2666 3666 -4666 -5666 6666 7666 8666 9666
7 7 8 | 667 -1667 -2667 3667 -4667 -5667 6667 7667 8667 9667
8 7 8 | 668 -1668 -2668 3668 -4668 -5668 6668 7668 8668 9668
9 7 8 | 669 -1669 -2669 3669 -4669 -5669 6669 7669 8669 9669
0 8 8 | -445 -1445 2445 3445 4445 -5445 6445 -7445 8445 9445
1 8 8 | -444 -1444 2444 3444 4444 -5444 6444 -7444 8444 9444
2 8 8 | -443 -1443 2443 3443 4443 -5443 6443 -7443 8443 9443
3 8 8 | 653 -1653 -2653 3653 -4653 -5653 6653 7653 8653 9653
4 8 8 | 654 -1654 -2654 3654 -4654 -5654 6654 7654 8654 9654
5 8 8 | 655 -1655 -2655 3655 -4655 -5655 6655 7655 8655 9655
6 8 8 | 656 -1656 -2656 3656 -4656 -5656 6656 7656 8656 9656
7 8 8 | 657 -1657 -2657 3657 -4657 -5657 6657 7657 8657 9657
8 8 8 | 658 -1658 -2658 3658 -4658 -5658 6658 7658 8658 9658
9 8 8 | 659 -1659 -2659 3659 -4659 -5659 6659 7659 8659 9659
0 9 8 | -455 -1455 2455 3455 4455 -5455 6455 -7455 8455 9455
1 9 8 | -454 -1454 2454 3454 4454 -5454 6454 -7454 8454 9454
2 9 8 | -453 -1453 2453 3453 4453 -5453 6453 -7453 8453 9453
3 9 8 | 643 -1643 -2643 3643 -4643 -5643 6643 7643 8643 9643
4 9 8 | 644 -1644 -2644 3644 -4644 -5644 6644 7644 8644 9644
5 9 8 | 645 -1645 -2645 3645 -4645 -5645 6645 7645 8645 9645
6 9 8 | 646 -1646 -2646 3646 -4646 -5646 6646 7646 8646 9646
7 9 8 | 647 -1647 -2647 3647 -4647 -5647 6647 7647 8647 9647
8 9 8 | 648 -1648 -2648 3648 -4648 -5648 6648 7648 8648 9648
9 9 8 | 649 -1649 -2649 3649 -4649 -5649 6649 7649 8649 9649
0 0 9 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 0 9 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 0 9 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 0 9 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 0 9 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 0 9 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 0 9 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 0 9 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 0 9 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 0 9 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 1 9 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 1 9 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 1 9 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 1 9 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 1 9 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 1 9 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 1 9 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 1 9 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 1 9 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 1 9 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
0 2 9 | -565 -1565 2565 3565 4565 -5565 6565 -7565 8565 9565
1 2 9 | -564 -1564 2564 3564 4564 -5564 6564 -7564 8564 9564
2 2 9 | -563 -1563 2563 3563 4563 -5563 6563 -7563 8563 9563
3 2 9 | 333 -1333 -2333 3333 -4333 -5333 6333 7333 8333 9333
4 2 9 | 334 -1334 -2334 3334 -4334 -5334 6334 7334 8334 9334
5 2 9 | 335 -1335 -2335 3335 -4335 -5335 6335 7335 8335 9335
6 2 9 | 336 -1336 -2336 3336 -4336 -5336 6336 7336 8336 9336
7 2 9 | 337 -1337 -2337 3337 -4337 -5337 6337 7337 8337 9337
8 2 9 | 338 -1338 -2338 3338 -4338 -5338 6338 7338 8338 9338
9 2 9 | 339 -1339 -2339 3339 -4339 -5339 6339 7339 8339 9339
0 3 9 | -365 1365 -2365 3365 -4365 5365 6365 -7365 8365 9365
1 3 9 | -364 1364 -2364 3364 -4364 5364 6364 -7364 8364 9364
2 3 9 | -363 1363 -2363 3363 -4363 5363 6363 -7363 8363 9363
3 3 9 | 533 1533 2533 3533 4533 5533 6533 7533 8533 9533
4 3 9 | 534 1534 2534 3534 4534 5534 6534 7534 8534 9534
5 3 9 | 535 1535 2535 3535 4535 5535 6535 7535 8535 9535
6 3 9 | 536 1536 2536 3536 4536 5536 6536 7536 8536 9536
7 3 9 | 537 1537 2537 3537 4537 5537 6537 7537 8537 9537
8 3 9 | 538 1538 2538 3538 4538 5538 6538 7538 8538 9538
9 3 9 | 539 1539 2539 3539 4539 5539 6539 7539 8539 9539
0 4 9 | -355 1355 -2355 3355 -4355 5355 6355 -7355 8355 9355
1 4 9 | -354 1354 -2354 3354 -4354 5354 6354 -7354 8354 9354
2 4 9 | -353 1353 -2353 3353 -4353 5353 6353 -7353 8353 9353
3 4 9 | 543 1543 2543 3543 4543 5543 6543 7543 8543 9543
4 4 9 | 544 1544 2544 3544 4544 5544 6544 7544 8544 9544
5 4 9 | 545 1545 2545 3545 4545 5545 6545 7545 8545 9545
6 4 9 | 546 1546 2546 3546 4546 5546 6546 7546 8546 9546
7 4 9 | 547 1547 2547 3547 4547 5547 6547 7547 8547 9547
8 4 9 | 548 1548 2548 3548 4548 5548 6548 7548 8548 9548
9 4 9 | 549 1549 2549 3549 4549 5549 6549 7549 8549 9549
0 5 9 | -345 1345 -2345 3345 -4345 5345 6345 -7345 8345 9345
1 5 9 | -344 1344 -2344 3344 -4344 5344 6344 -7344 8344 9344
2 5 9 | -343 1343 -2343 3343 -4343 5343 6343 -7343 8343 9343
3 5 9 | 553 1553 2553 3553 4553 5553 6553 7553 8553 9553
4 5 9 | 554 1554 2554 3554 4554 5554 6554 7554 8554 9554
5 5 9 | 555 1555 2555 3555 4555 5555 6555 7555 8555 9555
6 5 9 | 556 1556 2556 3556 4556 5556 6556 7556 8556 9556
7 5 9 | 557 1557 2557 3557 4557 5557 6557 7557 8557 9557
8 5 9 | 558 1558 2558 3558 4558 5558 6558 7558 8558 9558
9 5 9 | 559 1559 2559 3559 4559 5559 6559 7559 8559 9559
0 6 9 | -335 1335 -2335 3335 -4335 5335 6335 -7335 8335 9335
1 6 9 | -334 1334 -2334 3334 -4334 5334 6334 -7334 8334 9334
2 6 9 | -333 1333 -2333 3333 -4333 5333 6333 -7333 8333 9333
3 6 9 | 563 1563 2563 3563 4563 5563 6563 7563 8563 9563
4 6 9 | 564 1564 2564 3564 4564 5564 6564 7564 8564 9564
5 6 9 | 565 1565 2565 3565 4565 5565 6565 7565 8565 9565
6 6 9 | 566 1566 2566 3566 4566 5566 6566 7566 8566 9566
7 6 9 | 567 1567 2567 3567 4567 5567 6567 7567 8567 9567
8 6 9 | 568 1568 2568 3568 4568 5568 6568 7568 8568 9568
9 6 9 | 569 1569 2569 3569 4569 5569 6569 7569 8569 9569
0 7 9 | -535 -1535 2535 3535 4535 -5535 6535 -7535 8535 9535
1 7 9 | -534 -1534 2534 3534 4534 -5534 6534 -7534 8534 9534
2 7 9 | -533 -1533 2533 3533 4533 -5533 6533 -7533 8533 9533
3 7 9 | 363 -1363 -2363 3363 -4363 -5363 6363 7363 8363 9363
4 7 9 | 364 -1364 -2364 3364 -4364 -5364 6364 7364 8364 9364
5 7 9 | 365 -1365 -2365 3365 -4365 -5365 6365 7365 8365 9365
6 7 9 | 366 -1366 -2366 3366 -4366 -5366 6366 7366 8366 9366
7 7 9 | 367 -1367 -2367 3367 -4367 -5367 6367 7367 8367 9367
8 7 9 | 368 -1368 -2368 3368 -4368 -5368 6368 7368 8368 9368
9 7 9 | 369 -1369 -2369 3369 -4369 -5369 6369 7369 8369 9369
0 8 9 | -545 -1545 2545 3545 4545 -5545 6545 -7545 8545 9545
1 8 9 | -544 -1544 2544 3544 4544 -5544 6544 -7544 8544 9544
2 8 9 | -543 -1543 2543 3543 4543 -5543 6543 -7543 8543 9543
3 8 9 | 353 -1353 -2353 3353 -4353 -5353 6353 7353 8353 9353
4 8 9 | 354 -1354 -2354 3354 -4354 -5354 6354 7354 8354 9354
5 8 9 | 355 -1355 -2355 3355 -4355 -5355 6355 7355 8355 9355
6 8 9 | 356 -1356 -2356 3356 -4356 -5356 6356 7356 8356 9356
7 8 9 | 357 -1357 -2357 3357 -4357 -5357 6357 7357 8357 9357
8 8 9 | 358 -1358 -2358 3358 -4358 -5358 6358 7358 8358 9358
9 8 9 | 359 -1359 -2359 3359 -4359 -5359 6359 7359 8359 9359
0 9 9 | -555 -1555 2555 3555 4555 -5555 6555 -7555 8555 9555
1 9 9 | -554 -1554 2554 3554 4554 -5554 6554 -7554 8554 9554
2 9 9 | -553 -1553 2553 3553 4553 -5553 6553 -7553 8553 9553
3 9 9 | 343 -1343 -2343 3343 -4343 -5343 6343 7343 8343 9343
4 9 9 | 344 -1344 -2344 3344 -4344 -5344 6344 7344 8344 9344
5 9 9 | 345 -1345 -2345 3345 -4345 -5345 6345 7345 8345 9345
6 9 9 | 346 -1346 -2346 3346 -4346 -5346 6346 7346 8346 9346
7 9 9 | 347 -1347 -2347 3347 -4347 -5347 6347 7347 8347 9347
8 9 9 | 348 -1348 -2348 3348 -4348 -5348 6348 7348 8348 9348
9 9 9 | 349 -1349 -2349 3349 -4349 -5349 6349 7349 8349 9349
typedef struct __ghostzone_map__ {
short i0,i1,i2; // i0,i1,i2 stores values from -1 (used to indicate outer boundary)
// to Nxx_plus_2NGHOSTS*. We assume that grid extents beyond the
// limits of short (i.e., beyond about 32,000) are unlikely. This
// can be easily extended if needed, though.
} gz_map;
const int8_t MAXFACE = -1;
const int8_t NUL = +0;
const int8_t MINFACE = +1;
typedef struct __parity__ {
int8_t parity[10]; // We store the 10 parity conditions in 10 int8_t integers,
// one for each condition. Note that these conditions can
// only take one of two values: +1 or -1, hence the use of
// int8_t, the smallest C data type.
} parity_condition;
typedef struct __inner_bc__ {
gz_map inner_bc_dest_pt;
gz_map inner_bc_src_pt;
int8_t parity[10]; // We store the 10 parity conditions in 10 int8_t integers,
// one for each condition. Note that these conditions can
// only take one of two values: +1 or -1, hence the use of
// int8_t, the smallest C data type.
} inner_bc;
typedef struct __outer_bc__ {
gz_map outer_bc_dest_pt;
int8_t FACEi0,FACEi1,FACEi2; // FACEi* takes values of -1, 0, and +1 only,
// corresponding to MAXFACE, NUL, and MINFACE
// respectively.
// Thus int8_t (one byte each, the smallest C
// type) is sufficient.
} outer_bc;
typedef struct __bcstruct__ {
outer_bc **outer; // Array of 1D arrays, of length
// [NGHOSTS][num_ob_gz_pts[which_outer_ghostzone_point]]
inner_bc **inner; // Array of 1D arrays, of length
// [NGHOSTS][num_ib_gz_pts[which_inner_ghostzone_point]]
// Arrays storing number of outer/inner boundary ghostzone points at each ghostzone,
// of length NGHOSTS:
int *num_ob_gz_pts;
int *num_ib_gz_pts;
} bc_struct;
#include "BCs_data_structs.h"
#include "EigenCoord_xxCart.h"
#include "set_up__bc_gz_map_and_parity_condns.h"
#include "set_bcstruct.h"
#include "apply_bcs_curvilinear.h"
// Declare boundary condition BC_UPDATE_OUTER macro,
// which updates a single outer boundary face
// of the 3D grid cube using quadratic polynomial
// extrapolation.
#define BC_UPDATE_OUTER(which_gf, i0,i1,i2, FACEX0,FACEX1,FACEX2) { \
const int idx3 = IDX3S(i0,i1,i2); \
gfs[IDX4S(which_gf,i0,i1,i2)] = \
+3.0*gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)] \
-3.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)]; \
}
// Curvilinear boundary condition driver routine: Apply BCs to all six
// boundary faces of the 3D numerical domain, filling in the
// innermost ghost zone layer first, and moving outward.
void apply_bcs_curvilinear(const paramstruct *restrict params, const bc_struct *restrict bcstruct,
const int NUM_GFS, const int8_t *restrict gfs_parity, REAL *restrict gfs) {
#pragma omp parallel for
for(int which_gf=0;which_gf<NUM_GFS;which_gf++) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// First apply OUTER boundary conditions,
// in case an INNER (parity) boundary point
// needs data at the outer boundary:
// After updating each face, adjust imin[] and imax[]
// to reflect the newly-updated face extents.
for(int pt=0;pt<bcstruct->num_ob_gz_pts[which_gz];pt++) {
BC_UPDATE_OUTER(which_gf,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i0,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i1,
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i2,
bcstruct->outer[which_gz][pt].FACEi0,
bcstruct->outer[which_gz][pt].FACEi1,
bcstruct->outer[which_gz][pt].FACEi2);
}
// Then apply INNER (parity) boundary conditions:
for(int pt=0;pt<bcstruct->num_ib_gz_pts[which_gz];pt++) {
const int i0dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i0;
const int i1dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i1;
const int i2dest = bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i2;
const int i0src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i0;
const int i1src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i1;
const int i2src = bcstruct->inner[which_gz][pt].inner_bc_src_pt.i2;
const int8_t *prty= bcstruct->inner[which_gz][pt].parity;
// printf("%d\n",bcstruct->inner_bc_parity[which_gz][pt].parity[gfs_parity[which_gf]]);
gfs[IDX4S(which_gf,i0dest,i1dest,i2dest)] =
bcstruct->inner[which_gz][pt].parity[gfs_parity[which_gf]] * gfs[IDX4S(which_gf, i0src,i1src,i2src)];
} // END for(int pt=0;pt<num_ib_gz_pts[which_gz];pt++)
} // END for(int which_gz = 0; which_gz < NGHOSTS; which_gz++)
} // END for(int which_gf=0;which_gf<NUM_GFS;which_gf++)
} // END function
for(int i=0;i<NGHOSTS;i++) { free(bcstruct.outer[i]); free(bcstruct.inner[i]); }
free(bcstruct.num_ob_gz_pts); free(bcstruct.num_ib_gz_pts);
// Step 1: Allocate memory storage for bc_gz_map, which
// in the case a boundary point is a *parity*
// boundary, is set to the interior, non-
// boundary point corresponding to the same
// Cartesian gridpoint. Otherwise bc_gz_map
// is set to (i0,i1,i2) = (-1,-1,-1).
gz_map *bc_gz_map = (gz_map *)malloc(sizeof(gz_map)*Nxx_plus_2NGHOSTS_tot);
// Step 2: Allocate memory storage for bc_parity_conditions,
// which store parity conditions for all 10
// gridfunction types at all grid points.
parity_condition *bc_parity_conditions = (parity_condition *)malloc(sizeof(parity_condition)*Nxx_plus_2NGHOSTS_tot);
// Step 3: Set bc_gz_map and bc_parity_conditions at *all*
// points; on the boundary and otherwise.
set_up__bc_gz_map_and_parity_condns(¶ms, xx, bc_gz_map,
bc_parity_conditions
);
// Step 4: Declare and allocate memory for bcstruct,
// which will store all information needed for
// applying the boundary conditions.
bcstruct.outer = (outer_bc **)malloc(sizeof(outer_bc *)*NGHOSTS);
bcstruct.inner = (inner_bc **)malloc(sizeof(inner_bc *)*NGHOSTS);
bcstruct.num_ob_gz_pts = ( int *)malloc(sizeof(int)*NGHOSTS);
bcstruct.num_ib_gz_pts = ( int *)malloc(sizeof(int)*NGHOSTS);
// Step 4: Store all information needed to quickly and
// efficiently apply boundary conditions. This
// function transfers all information from
// bc_gz_map (defined at *all gridpoints*) into
// bcstruct (defined only at boundary points).
// Thus when this function has finished,
// bc_gz_map is no longer needed.
set_bcstruct(¶ms,bc_gz_map,
bc_parity_conditions,
&bcstruct);
// Step 5: As described in Step 4, bc_gz_map is no
// longer needed at this point, so we free its
// memory. Farewell, friend!
free(bc_gz_map);
free(bc_parity_conditions);
// set_bcstruct() loops from the innermost boundary
// ghostzones on the cube ("which_gz==0",
// corresponding to the single layer of ghostzones
// closest to the interior data), and at each
// ghostzone layer, we apply the following 5-step
// algorithm:
// Step 1: Count the number of outer and inner
// boundary points, store to
// num_ob_pts and num_ib_pts, respectively.
// Step 2: Now that we know the number of outer
// boundary points on this ghostzone layer,
// allocate memory needed for storing the
// outer and inner boundary condition data.
// Step 2.a: At all outer boundary ghost zones, allocate
// memory for a single member of the outer_bc
// data type.
// Step 2.b: At all inner boundary ghost zones, allocate
// memory for a single member of the inner_bc
// data type.
// Step 3: Store the number of outer and inner boundary
// points on each ghostzone layer, where e.g.,
// which_gz==0 corresponds to the innermost
// ghostzones on the numerical domain.
// Step 4: Store information needed for outer boundary
// conditions, to outer_bc_dest_pt and
// outer_bc_face arrays.
// Step 5: Store information needed for inner boundary
// conditions, including interior point to which
// inner ghost zone maps, and parity conditions
// for all 10 gridfunction types.
void set_bcstruct(const paramstruct *restrict params,
gz_map *restrict bc_gz_map,
parity_condition *bc_parity_conditions,
bc_struct *restrict bcstruct) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
// Loop from the innermost ghostzone on the cube (which_gz==0) and work outward.
// This ordering is necessary, as ghostzones at which_gz==1 will generally
// depend on ghostzones at which_gz==0 being already set.
for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
// Step 1: Count the number of outer and inner
// boundary points, store to
// num_ob_pts and num_ib_pts, respectively.
#define COUNT_INNER_OR_OUTER if(bc_gz_map[IDX3S(i0,i1,i2)].i0==-1) { num_ob_pts++;} else { num_ib_pts++; }
int num_ob_pts = 0;
int num_ib_pts = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) { COUNT_INNER_OR_OUTER } imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) { COUNT_INNER_OR_OUTER } imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) { COUNT_INNER_OR_OUTER } imax[2]++;
// Step 2: Now that we know the number of outer boundary points on this ghostzone
// layer, we allocate memory needed for storing the outer and inner boundary
// condition data.
// Step 2.a: At all outer boundary ghost zones, allocate memory for a single member of the outer_bc
// data type.
bcstruct->outer[which_gz] = (outer_bc *)malloc(sizeof(outer_bc)*num_ob_pts);
// Step 2.b: At all inner boundary ghost zones, allocate memory for a single member of the inner_bc
// data type.
bcstruct->inner[which_gz] = (inner_bc *)malloc(sizeof(inner_bc)*num_ib_pts);
// Step 3: Store the number of outer and inner boundary points on each ghostzone layer, where e.g.,
// which_gz==0 corresponds to the innermost ghostzones on the numerical domain.
bcstruct->num_ob_gz_pts[which_gz] = num_ob_pts;
bcstruct->num_ib_gz_pts[which_gz] = num_ib_pts;
// Reset imin[] and imax[], to prepare for the next step.
for(int ii=0;ii<3;ii++) {imin[ii]++; imax[ii]--;}
// Step 4: Store information needed for outer boundary conditions, to outer_bc_dest_pt[which_gz][]
// and outer_bc_face[which_gz][] arrays:
#define OB_SET(facei0,facei1,facei2) if(bc_gz_map[IDX3S(i0,i1,i2)].i0==-1) { \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i0 = i0; \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i1 = i1; \
bcstruct->outer[which_gz][pt].outer_bc_dest_pt.i2 = i2; \
bcstruct->outer[which_gz][pt].FACEi0= facei0; \
bcstruct->outer[which_gz][pt].FACEi1= facei1; \
bcstruct->outer[which_gz][pt].FACEi2= facei2; \
pt++; }
int pt = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) {OB_SET(MINFACE,NUL,NUL)} imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) {OB_SET(MAXFACE,NUL,NUL)} imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) {OB_SET(NUL,MINFACE,NUL)} imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) {OB_SET(NUL,MAXFACE,NUL)} imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) {OB_SET(NUL,NUL,MINFACE)} imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) {OB_SET(NUL,NUL,MAXFACE)} imax[2]++;
// fprintf(stderr,"num OB points with which_gz = %d: %d | should be: %d\n",which_gz,pt,num_ob_gz_pts[which_gz]);
// Reset imin[] and imax[], to prepare for the next step.
for(int ii=0;ii<3;ii++) {imin[ii]++; imax[ii]--;}
// Step 5: Store information needed for inner boundary conditions, including interior point to which
// inner ghost zone maps, and parity conditions for all 10 gridfunction types.
#define IB_SET if(bc_gz_map[IDX3S(i0,i1,i2)].i0!=-1) { \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i0=i0; \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i1=i1; \
bcstruct->inner[which_gz][pt].inner_bc_dest_pt.i2=i2; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i0 =bc_gz_map[IDX3S(i0,i1,i2)].i0; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i1 =bc_gz_map[IDX3S(i0,i1,i2)].i1; \
bcstruct->inner[which_gz][pt].inner_bc_src_pt.i2 =bc_gz_map[IDX3S(i0,i1,i2)].i2; \
for(int ii=0;ii<10;ii++) { \
bcstruct->inner[which_gz][pt].parity[ii] = \
(int8_t)bc_parity_conditions[IDX3S(i0,i1,i2)].parity[ii]; } \
pt++; }
pt = 0;
LOOP_REGION(imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]) {IB_SET} imin[0]--;
LOOP_REGION(imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]) {IB_SET} imax[0]++;
LOOP_REGION(imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]) {IB_SET} imin[1]--;
LOOP_REGION(imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]) {IB_SET} imax[1]++;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]) {IB_SET} imin[2]--;
LOOP_REGION(imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1) {IB_SET} imax[2]++;
} // END for(int which_gz = 0; which_gz < NGHOSTS; which_gz++)
} // END function
// set_parity_conditions_from_symbolic_dot_products():
// Evaluate dot products needed for setting parity
// conditions at a given point (xx0,xx1,xx2),
// using C code generated by NRPy+ function
// parity_conditions_symbolic_dot_products().
void eval_symbolic_dot_products_to_set_parity_conditions(const paramstruct *restrict params, REAL parity[10],
const REAL xx0,const REAL xx1,const REAL xx2,
const REAL xx0_inbounds,const REAL xx1_inbounds,const REAL xx2_inbounds) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
#include "parity_conditions_symbolic_dot_products.h"
}
void set_up__bc_gz_map_and_parity_condns(const paramstruct *restrict params,
REAL *xx[3], gz_map *bc_gz_map,parity_condition *bc_parity_conditions) {
#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
* accounting for the relative path */
// xx[0][j] = xxmin[0] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*dxx0;
// -> xxmin[0] = xx[0][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx0
const REAL xxmin[3] = { xx[0][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx0,
xx[1][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx1,
xx[2][0] - ((REAL)(0-NGHOSTS) + (1.0/2.0))*dxx2 };
//fprintf(stderr,"hey inside setbc: %e %e %e | %e %e\n",xxmin[0],xxmin[1],xxmin[2],xx[0][0],dxx0);
LOOP_REGION(0,Nxx_plus_2NGHOSTS0,0,Nxx_plus_2NGHOSTS1,0,Nxx_plus_2NGHOSTS2) {
// Step 1: Convert the (curvilinear) coordinate (x0,x1,x2) to Cartesian coordinates
REAL xCart[3];
EigenCoord_xxCart(params, xx, i0,i1,i2, xCart);
REAL Cartx = xCart[0];
REAL Carty = xCart[1];
REAL Cartz = xCart[2];
// Step 2: Find the (i0_inbounds,i1_inbounds,i2_inbounds) corresponding to the above Cartesian coordinate.
// If (i0_inbounds,i1_inbounds,i2_inbounds) is in a ghost zone, then it must equal (i0,i1,i2), and
// the point is an outer boundary point.
// Otherwise (i0_inbounds,i1_inbounds,i2_inbounds) is in the grid interior, and data at (i0,i1,i2)
// must be replaced with data at (i0_inbounds,i1_inbounds,i2_inbounds), but multiplied by the
// appropriate parity condition (+/- 1).
REAL Cart_to_xx0_inbounds,Cart_to_xx1_inbounds,Cart_to_xx2_inbounds;
#include "EigenCoord_Cart_to_xx.h"
int i0_inbounds = (int)( (Cart_to_xx0_inbounds - xxmin[0] - (1.0/2.0)*dxx0 + ((REAL)NGHOSTS)*dxx0)/dxx0 + 0.5 );
int i1_inbounds = (int)( (Cart_to_xx1_inbounds - xxmin[1] - (1.0/2.0)*dxx1 + ((REAL)NGHOSTS)*dxx1)/dxx1 + 0.5 );
int i2_inbounds = (int)( (Cart_to_xx2_inbounds - xxmin[2] - (1.0/2.0)*dxx2 + ((REAL)NGHOSTS)*dxx2)/dxx2 + 0.5 );
// Step 2.a: (Sanity/validation check) Convert the interior point
// x0(i0_inbounds),x1(i1_inbounds),x2(i2_inbounds) to Cartesian coordinates,
// make sure that the Cartesian coordinate matches the Cartesian coordinate of
// x0(i0),x1(i1),x2(i2). If not, error out!
REAL xCart_orig[3]; for(int ii=0;ii<3;ii++) xCart_orig[ii] = xCart[ii];
EigenCoord_xxCart(params, xx, i0_inbounds,i1_inbounds,i2_inbounds, xCart);
//fprintf(stderr,"Cartesian agreement: ( %.15e %.15e %.15e ) ?= ( %.15e %.15e %.15e )\n",
// (double)xCart_orig[0],(double)xCart_orig[1],(double)xCart_orig[2],
// (double)xCart[0],(double)xCart[1],(double)xCart[2]);
#define EPS_ABS 1e-8
if(fabs( (double)(xCart_orig[0] - xCart[0]) ) > EPS_ABS ||
fabs( (double)(xCart_orig[1] - xCart[1]) ) > EPS_ABS ||
fabs( (double)(xCart_orig[2] - xCart[2]) ) > EPS_ABS) {
fprintf(stderr,"Error. Cartesian disagreement: ( %.15e %.15e %.15e ) != ( %.15e %.15e %.15e )\n",
(double)xCart_orig[0],(double)xCart_orig[1],(double)xCart_orig[2],
(double)xCart[0],(double)xCart[1],(double)xCart[2]);
exit(1);
}
// Step 3: Set bc_gz_map and bc_parity_conditions.
if(i0_inbounds-i0 == 0 && i1_inbounds-i1 == 0 && i2_inbounds-i2 == 0) {
// Step 3.a: Iff we are on an outer boundary point or in the grid
// interior, i0_inbounds==i0, i1_inbounds==i1, and
// i2_inbounds==i2, and inner boundary conditions do not
// apply: set bc_gz_map to -1, and parity=1.
bc_gz_map[IDX3S(i0,i1,i2)].i0=-1;
bc_gz_map[IDX3S(i0,i1,i2)].i1=-1;
bc_gz_map[IDX3S(i0,i1,i2)].i2=-1;
for(int which_parity=0; which_parity<10; which_parity++) {
bc_parity_conditions[IDX3S(i0,i1,i2)].parity[which_parity] = 1;
}
} else {
// Step 3.b: If we are on an *inner* boundary point:
// 1. Set bc_gz_map at (i0,i1,i2) to the point
// in the interior to which this boundary
// point maps, and
// 2. Perform the unit vector dot products
// necessary to set all 10 possible parity
// conditions, calling function
// set_parity_from_unit_vector_dot_product()
bc_gz_map[IDX3S(i0,i1,i2)].i0=i0_inbounds;
bc_gz_map[IDX3S(i0,i1,i2)].i1=i1_inbounds;
bc_gz_map[IDX3S(i0,i1,i2)].i2=i2_inbounds;
const REAL xx0 = xx[0][i0];
const REAL xx1 = xx[1][i1];
const REAL xx2 = xx[2][i2];
const REAL xx0_inbounds = xx[0][i0_inbounds];
const REAL xx1_inbounds = xx[1][i1_inbounds];
const REAL xx2_inbounds = xx[2][i2_inbounds];
REAL REAL_parity_array[10];
eval_symbolic_dot_products_to_set_parity_conditions(params, REAL_parity_array, xx0,xx1,xx2,
xx0_inbounds,xx1_inbounds,xx2_inbounds);
for(int whichparity=0;whichparity<10;whichparity++) {
//printf("Good? Parity %d evaluated to %e\n",whichparity,(double)REAL_parity_array[whichparity]);
// Perform sanity check on parity array output: should be +1 or -1 to within 8 significant digits:
if( (REAL_parity_array[whichparity] > 0 && fabs(REAL_parity_array[whichparity] - (+1)) > 1e-8) ||
(REAL_parity_array[whichparity] <= 0 && fabs(REAL_parity_array[whichparity] - (-1)) > 1e-8) ) {
fprintf(stderr,"Error. Parity evaluated to %e , which is not within 8 significant digits of +1 or -1.",
REAL_parity_array[whichparity]);
exit(1);
}
if(REAL_parity_array[whichparity] < 0.0) bc_parity_conditions[IDX3S(i0,i1,i2)].parity[whichparity] = -1;
if(REAL_parity_array[whichparity] > 0.0) bc_parity_conditions[IDX3S(i0,i1,i2)].parity[whichparity] = +1;
}
}
}
}
# As documented in the NRPy+ tutorial module
# Tutorial-RK_Butcher_Table_Generating_C_Code.ipynb,
# this module will produce the required C codes for
# allocating required memory Method of Lines (MoL) timestepping,
# implementing MoL timestepping, and deallocating memory
# Authors: Brandon Clark
# Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Initialize needed Python/NRPy+ modules
import sympy as sp # Import SymPy, a computer algebra system written entirely in Python
import os # Standard Python module for multiplatform OS-level functions
from MoLtimestepping.RK_Butcher_Table_Dictionary import Butcher_dict
# Step 2: Checking if Butcher Table is Diagonal
def diagonal(key):
diagonal = True # Start with the Butcher table is diagonal
Butcher = Butcher_dict[key][0]
L = len(Butcher)-1 # Establish the number of rows to check for diagonal trait, all bust last row
row_idx = 0 # Initialize the Butcher table row index
for i in range(L): # Check all the desired rows
for j in range(1,row_idx): # Check each element before the diagonal element in a row
if Butcher[i][j] != sp.sympify(0): # If any non-diagonal coeffcient is non-zero,
# then the table is not diagonal
diagonal = False
return diagonal
row_idx += 1 # Update to check the next row
return diagonal
# Step 3: When allocating memory, we populate a list malloced_gridfunctions,
# which is used here to determine which gridfunctions need memory freed,
# via the free() command. Free the mallocs!
def free_allocated_memory(outdir,RK_method,malloced_gridfunctions):
# This step is made extremely easy, as we had to
with open(os.path.join(outdir, "RK_Free_Memory.h"), "w") as file:
file.write("// Code snippet freeing gridfunction memory for \"" + RK_method + "\" method:\n")
for gridfunction in malloced_gridfunctions:
file.write("free(" + gridfunction + ");\n")
# # State whether each Butcher table is diagonal or not
# for key, value in Butcher_dict.items():
# if diagonal(key) == True:
# print("The RK method "+str(key)+" is diagonal! \n")
# else:
# print("The RK method "+str(key)+" is NOT diagonal! \n")
# #################################################################
# Step 4: Main driver function for outputting all the MoL C Code
def MoL_C_Code_Generation(RK_method = "RK4", RHS_string = "", post_RHS_string = "",outdir="MoLtimestepping/",
MemAllocOnly=False):
####### Step 3.a: Allocating Memory
malloc_str = "// Code snippet allocating gridfunction memory for \"" + RK_method + "\" method:\n"
# Loop over grids
malloced_gridfunctions = []
# Set gridfunction type
type_str = "REAL *restrict "
# Define a couple useful functions for outputting the needed C code for allocating memory
def malloc_gfs_str(varname):
malloced_gridfunctions.append(varname)
memory_alloc_str = " = (REAL *)malloc(sizeof(REAL) * NUM_EVOL_GFS * Nxx_plus_2NGHOSTS_tot"+")"
return type_str + varname + memory_alloc_str + ";\n"
def diagnostic_output_gfs_equal_to(gfs):
return type_str + "diagnostic_output_gfs"+" = "+gfs + ";\n"
# No matter the method we define gridfunctions "y_n_gfs" to store the initial data
malloc_str += malloc_gfs_str("y_n_gfs")
if diagonal(RK_method) == True and "RK3" in RK_method:
malloc_str += malloc_gfs_str("k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs")
malloc_str += malloc_gfs_str("k2_or_y_nplus_a32_k2_gfs")
malloc_str += diagnostic_output_gfs_equal_to("k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs")
else:
if diagonal(RK_method) == False: # Allocate memory for non-diagonal Butcher tables
# Determine the number of k_i steps based on length of Butcher Table
num_k = len(Butcher_dict[RK_method][0])-1
# For non-diagonal tables an intermediate gridfunction "next_y_input" is used for rhs evaluations
malloc_str += malloc_gfs_str("next_y_input_gfs")
for i in range(num_k): # Need to allocate all k_i steps for a given method
malloc_str += malloc_gfs_str("k"+str(i+1)+"_gfs")
malloc_str += diagnostic_output_gfs_equal_to("k1_gfs")
else: # Allocate memory for diagonal Butcher tables, which use a "y_nplus1_running_total gridfunction"
malloc_str += malloc_gfs_str("y_nplus1_running_total_gfs")
if RK_method != 'Euler': # Allocate memory for diagonal Butcher tables that aren't Euler
# Need k_odd for k_1,3,5... and k_even for k_2,4,6...
malloc_str += malloc_gfs_str("k_odd_gfs")
malloc_str += malloc_gfs_str("k_even_gfs")
malloc_str += diagnostic_output_gfs_equal_to("y_nplus1_running_total_gfs")
with open(os.path.join(outdir,"RK_Allocate_Memory.h"), "w") as file:
file.write(malloc_str)
if MemAllocOnly:
free_allocated_memory(outdir,RK_method,malloced_gridfunctions)
return
########################################################################################################################
# EXAMPLE
# ODE: y' = f(t,y), y(t_0) = y_0
# Starting at time t_n with solution having value y_n and trying to update to y_nplus1 with timestep dt
# Example of scheme for RK4 with k_1, k_2, k_3, k_4 (Using non-diagonal algortihm) Notice this requires storage of
# y_n, y_nplus1, k_1 through k_4
# k_1 = dt*f(t_n, y_n)
# k_2 = dt*f(t_n + 1/2*dt, y_n + 1/2*k_1)
# k_3 = dt*f(t_n + 1/2*dt, y_n + 1/2*k_2)
# k_4 = dt*f(t_n + dt, y_n + k_3)
# y_nplus1 = y_n + 1/3k_1 + 1/6k_2 + 1/6k_3 + 1/3k_4
# Example of scheme RK4 using only k_odd and k_even (Diagonal algroithm) Notice that this only requires storage
# k_odd = dt*f(t_n, y_n)
# y_nplus1 = 1/3*k_odd
# k_even = dt*f(t_n + 1/2*dt, y_n + 1/2*k_odd)
# y_nplus1 += 1/6*k_even
# k_odd = dt*f(t_n + 1/2*dt, y_n + 1/2*k_even)
# y_nplus1 += 1/6*k_odd
# k_even = dt*f(t_n + dt, y_n + k_odd)
# y_nplus1 += 1/3*k_even
########################################################################################################################
####### Step 3b: Implementing the Runge Kutta Scheme for Method of Lines Timestepping
Butcher = Butcher_dict[RK_method][0] # Get the desired Butcher table from the dictionary
num_steps = len(Butcher)-1 # Specify the number of required steps to update solution
indent = " "
RK_str = "// Code snippet implementing "+RK_method+" algorithm for Method of Lines timestepping\n"
# Diagonal RK3 only!!!
def single_RK_substep(commentblock, RHS_str, RHS_input_str, RHS_output_str, RK_lhss_list, RK_rhss_list,
post_RHS_list, post_RHS_output_list, indent = " "):
return_str = commentblock + "\n"
if not isinstance(RK_lhss_list,list):
RK_lhss_list = [RK_lhss_list]
if not isinstance(RK_rhss_list,list):
RK_rhss_list = [RK_rhss_list]
if not isinstance(post_RHS_list,list):
post_RHS_list = [post_RHS_list]
if not isinstance(post_RHS_output_list,list):
post_RHS_output_list = [post_RHS_output_list]
# Part 1: RHS evaluation:
return_str += RHS_str.replace("RK_INPUT_GFS", RHS_input_str).\
replace("RK_OUTPUT_GFS",RHS_output_str)+"\n"
# Part 2: RK update
return_str += "LOOP_ALL_GFS_GPS"+"(i) {\n"
for lhs,rhs in zip(RK_lhss_list,RK_rhss_list):
return_str += indent + lhs + "[i] = " + rhs.replace("_gfs","_gfs") + ";\n"
return_str += "}\n"
# Part 3: Call post-RHS functions
for post_RHS,post_RHS_output in zip(post_RHS_list,post_RHS_output_list):
return_str += post_RHS.replace("RK_OUTPUT_GFS",post_RHS_output)+"\n"
return return_str+"\n"
RK_str = "// C code implementation of " + RK_method + " Method of Lines timestepping.\n"
if diagonal(RK_method) == True and "RK3" in RK_method:
# In a diagonal RK3 method, only 3 gridfunctions need be defined. Below implements this approach.
# k_1
RK_str += """
// In a diagonal RK3 method like this one, only 3 gridfunctions need be defined. Below implements this approach.
// Using y_n_gfs as input, k1 and apply boundary conditions\n"""
RK_str += single_RK_substep(
commentblock = """
// ***k1 substep:***
// 1. We will store k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs now as
// ... the update for the next rhs evaluation y_n + a21*k1*dt
// Post-RHS evaluation:
// 1. Apply post-RHS to y_n + a21*k1*dt""",
RHS_str = RHS_string,
RHS_input_str = "y_n_gfs", RHS_output_str = "k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs",
RK_lhss_list = ["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"],
RK_rhss_list = ["("+sp.ccode(Butcher[1][1]).replace("L","")+")*k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i]*dt + y_n_gfs[i]"],
post_RHS_list = [post_RHS_string], post_RHS_output_list = ["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"])
# k_2
RK_str += single_RK_substep(
commentblock="""
// ***k2 substep:***
// 1. Reassign k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs to be the running total y_{n+1}; a32*k2*dt to the running total
// 2. Store k2_or_y_nplus_a32_k2_gfs now as y_n + a32*k2*dt
// Post-RHS evaluation:
// 1. Apply post-RHS to both y_n + a32*k2 (stored in k2_or_y_nplus_a32_k2_gfs)
// ... and the y_{n+1} running total, as they have not been applied yet to k2-related gridfunctions""",
RHS_str=RHS_string,
RHS_input_str="k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs", RHS_output_str="k2_or_y_nplus_a32_k2_gfs",
RK_lhss_list=["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs","k2_or_y_nplus_a32_k2_gfs"],
RK_rhss_list=["("+sp.ccode(Butcher[3][1]).replace("L","")+")*(k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i] - y_n_gfs[i])/("+sp.ccode(Butcher[1][1]).replace("L","")+") + y_n_gfs[i] + ("+sp.ccode(Butcher[3][2]).replace("L","")+")*k2_or_y_nplus_a32_k2_gfs[i]*dt",
"("+sp.ccode(Butcher[2][2]).replace("L","")+")*k2_or_y_nplus_a32_k2_gfs[i]*dt + y_n_gfs[i]"],
post_RHS_list=[post_RHS_string,post_RHS_string],
post_RHS_output_list=["k2_or_y_nplus_a32_k2_gfs","k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"])
# k_3
RK_str += single_RK_substep(
commentblock="""
// ***k3 substep:***
// 1. Add k3 to the running total and save to y_n
// Post-RHS evaluation:
// 1. Apply post-RHS to y_n""",
RHS_str=RHS_string,
RHS_input_str="k2_or_y_nplus_a32_k2_gfs", RHS_output_str="y_n_gfs",
RK_lhss_list=["y_n_gfs","k2_or_y_nplus_a32_k2_gfs"],
RK_rhss_list=["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i] + ("+sp.ccode(Butcher[3][3]).replace("L","")+")*y_n_gfs[i]*dt"],
post_RHS_list=[post_RHS_string],
post_RHS_output_list=["y_n_gfs"])
else:
y_n = "y_n_gfs"
if diagonal(RK_method) == False:
for s in range(num_steps):
next_y_input = "next_y_input_gfs"
# If we're on the first step (s=0), we use y_n gridfunction as input.
# Otherwise next_y_input is input. Output is just the reverse.
if s==0: # If on first step:
RHS_input = y_n
else: # If on second step or later:
RHS_input = next_y_input
RHS_output = "k" + str(s + 1) + "_gfs"
if s == num_steps-1: # If on final step:
RK_lhs = y_n
RK_rhs = y_n + "[i] + dt*("
else: # If on anything but the final step:
RK_lhs = next_y_input
RK_rhs = y_n + "[i] + dt*("
for m in range(s+1):
if Butcher[s+1][m+1] != 0:
if Butcher[s+1][m+1] != 1:
RK_rhs += " + k"+str(m+1)+"_gfs[i]*("+sp.ccode(Butcher[s+1][m+1]).replace("L","")+")"
else:
RK_rhs += " + k"+str(m+1)+"_gfs[i]"
RK_rhs += " )"
post_RHS = post_RHS_string
if s == num_steps-1: # If on final step:
post_RHS_output = y_n
else: # If on anything but the final step:
post_RHS_output = next_y_input
RK_str += single_RK_substep(
commentblock="// ***k" + str(s + 1) + " substep:***",
RHS_str=RHS_string,
RHS_input_str=RHS_input, RHS_output_str=RHS_output,
RK_lhss_list=[RK_lhs], RK_rhss_list=[RK_rhs],
post_RHS_list=[post_RHS],
post_RHS_output_list=[post_RHS_output])
else:
y_nplus1_running_total = "y_nplus1_running_total_gfs"
if RK_method == 'Euler': # Euler's method doesn't require any k_i, and gets its own unique algorithm
RK_str += single_RK_substep(
commentblock="// ***Euler timestepping only requires one RHS evaluation***",
RHS_str=RHS_string,
RHS_input_str=y_n, RHS_output_str=y_nplus1_running_total,
RK_lhss_list=[y_n], RK_rhss_list=[y_n+"[i] + "+y_nplus1_running_total+"[i]*dt"],
post_RHS_list=[post_RHS_string],
post_RHS_output_list=[y_n])
else:
for s in range(num_steps):
# If we're on the first step (s=0), we use y_n gridfunction as input.
# and k_odd as output.
if s == 0:
RHS_input = "y_n_gfs"
RHS_output = "k_odd_gfs"
# For the remaining steps the inputs and ouputs alternate between k_odd and k_even
elif s%2 == 0:
RHS_input = "k_even_gfs"
RHS_output = "k_odd_gfs"
else:
RHS_input = "k_odd_gfs"
RHS_output = "k_even_gfs"
RK_lhs_list = []
RK_rhs_list = []
if s != num_steps-1: # For anything besides the final step
if s == 0: # The first RK step
RK_lhs_list.append(y_nplus1_running_total)
RK_rhs_list.append(RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")
RK_lhs_list.append(RHS_output)
RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[s+1][s+1]).replace("L","")+")")
else:
if Butcher[num_steps][s+1] !=0:
RK_lhs_list.append(y_nplus1_running_total)
if Butcher[num_steps][s+1] !=1:
RK_rhs_list.append(y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")
else:
RK_rhs_list.append(y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt")
if Butcher[s+1][s+1] !=0:
RK_lhs_list.append(RHS_output)
if Butcher[s+1][s+1] !=1:
RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[s+1][s+1]).replace("L","")+")")
else:
RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt")
post_RHS_output = RHS_output
if s == num_steps-1: # If on the final step
if Butcher[num_steps][s+1] != 0:
RK_lhs_list.append(y_n)
if Butcher[num_steps][s+1] != 1:
RK_rhs_list.append(y_n+"[i] + "+y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")
else:
RK_rhs_list.append(y_n+"[i] + "+y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt)")
post_RHS_output = y_n
RK_str += single_RK_substep(
commentblock="// ***k" + str(s + 1) + " substep:***",
RHS_str=RHS_string,
RHS_input_str=RHS_input, RHS_output_str=RHS_output,
RK_lhss_list=RK_lhs_list, RK_rhss_list=RK_rhs_list,
post_RHS_list=[post_RHS_string],
post_RHS_output_list=[post_RHS_output])
with open(os.path.join(outdir,"RK_MoL.h"), "w") as file:
file.write(RK_str)
####### Step 3c: Freeing Allocated Memory
free_allocated_memory(outdir,RK_method,malloced_gridfunctions)
# As documented in the NRPy+ tutorial module
# Tutorial-RK_Butcher_Table_Dictionary.ipynb ,
# this module will construct a Python dictionary
# of Butcher tables for a large number of explicit
# RK methods.
# Authors: Brandon Clark
# Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Initialize needed Python/NRPy+ modules
import sympy as sp
import NRPy_param_funcs as par
# Step 2a: Generating a Dictionary of Butcher Tables for Explicit Runge Kutta Techniques
# Initialize the dictionary Butcher_dict
Butcher_dict = {}
# Step 2.a.i: Euler's Method
Butcher_dict['Euler'] = (
[[sp.sympify(0)],
["", sp.sympify(1)]]
, 1)
# Step 2.a.ii: RK2 Heun's Method
Butcher_dict['RK2 Heun'] = (
[[sp.sympify(0)],
[sp.sympify(1), sp.sympify(1)],
["", sp.Rational(1,2), sp.Rational(1,2)]]
, 2)
# Step 2.a.iii: RK2 Midpoint (MP) Method
Butcher_dict['RK2 MP'] = (
[[sp.sympify(0)],
[sp.Rational(1,2), sp.Rational(1,2)],
["", sp.sympify(0), sp.sympify(1)]]
, 2)
# Step 2.a.iv: RK2 Ralston's Method
Butcher_dict['RK2 Ralston'] = (
[[sp.sympify(0)],
[sp.Rational(2,3), sp.Rational(2,3)],
["", sp.Rational(1,4), sp.Rational(3,4)]]
, 2)
# Step 2.a.v: Kutta's Third-order Method
Butcher_dict['RK3'] = (
[[sp.sympify(0)],
[sp.Rational(1,2), sp.Rational(1,2)],
[sp.sympify(1), sp.sympify(-1), sp.sympify(2)],
["", sp.Rational(1,6), sp.Rational(2,3), sp.Rational(1,6)]]
, 3)
# Step 2.a.vi: RK3 Heun's Method
Butcher_dict['RK3 Heun'] = (
[[sp.sympify(0)],
[sp.Rational(1,3), sp.Rational(1,3)],
[sp.Rational(2,3), sp.sympify(0), sp.Rational(2,3)],
["", sp.Rational(1,4), sp.sympify(0), sp.Rational(3,4)]]
, 3)
# Step 2.a.vii: RK3 Ralton's Method
Butcher_dict['RK3 Ralston'] = ([[0],
[sp.Rational(1,2), sp.Rational(1,2)],
[sp.Rational(3,4), sp.sympify(0), sp.Rational(3,4)],
["", sp.Rational(2,9), sp.Rational(1,3), sp.Rational(4,9)]]
, 3)
# Step 2.a.viii: Strong Stability Preserving Runge-Kutta (SSPRK3) Method
Butcher_dict['SSPRK3'] = (
[[0],
[sp.sympify(1), sp.sympify(1)],
[sp.Rational(1,2), sp.Rational(1,4), sp.Rational(1,4)],
["", sp.Rational(1,6), sp.Rational(1,6), sp.Rational(2,3)]]
, 3)
# Step 2.a.ix: Classic RK4 Method
Butcher_dict['RK4'] = ([[sp.sympify(0)],
[sp.Rational(1,2), sp.Rational(1,2)],
[sp.Rational(1,2), sp.sympify(0), sp.Rational(1,2)],
[sp.sympify(1), sp.sympify(0), sp.sympify(0), sp.sympify(1)],
["", sp.Rational(1,6), sp.Rational(1,3), sp.Rational(1,3), sp.Rational(1,6)]]
, 4)
# Step 2.a.x: RK5 Dormand-Prince Method
Butcher_dict['DP5'] = ([[0],
[sp.Rational(1,5), sp.Rational(1,5)],
[sp.Rational(3,10),sp.Rational(3,40), sp.Rational(9,40)],
[sp.Rational(4,5), sp.Rational(44,45), sp.Rational(-56,15), sp.Rational(32,9)],
[sp.Rational(8,9), sp.Rational(19372,6561), sp.Rational(-25360,2187), sp.Rational(64448,6561), sp.Rational(-212,729)],
[sp.sympify(1), sp.Rational(9017,3168), sp.Rational(-355,33), sp.Rational(46732,5247), sp.Rational(49,176), sp.Rational(-5103,18656)],
[sp.sympify(1), sp.Rational(35,384), sp.sympify(0), sp.Rational(500,1113), sp.Rational(125,192), sp.Rational(-2187,6784), sp.Rational(11,84)],
["", sp.Rational(35,384), sp.sympify(0), sp.Rational(500,1113), sp.Rational(125,192), sp.Rational(-2187,6784), sp.Rational(11,84), sp.sympify(0)]]
, 5)
# Step 2.a.xi: RK5 Dormand-Prince Method Alternative
Butcher_dict['DP5alt'] = (
[[0],
[sp.Rational(1,10), sp.Rational(1,10)],
[sp.Rational(2,9), sp.Rational(-2, 81), sp.Rational(20, 81)],
[sp.Rational(3,7), sp.Rational(615, 1372), sp.Rational(-270, 343), sp.Rational(1053, 1372)],
[sp.Rational(3,5), sp.Rational(3243, 5500), sp.Rational(-54, 55), sp.Rational(50949, 71500), sp.Rational(4998, 17875)],
[sp.Rational(4, 5), sp.Rational(-26492, 37125), sp.Rational(72, 55), sp.Rational(2808, 23375), sp.Rational(-24206, 37125), sp.Rational(338, 459)],
[sp.sympify(1), sp.Rational(5561, 2376), sp.Rational(-35, 11), sp.Rational(-24117, 31603), sp.Rational(899983, 200772), sp.Rational(-5225, 1836), sp.Rational(3925, 4056)],
["", sp.Rational(821, 10800), sp.sympify(0), sp.Rational(19683, 71825), sp.Rational(175273, 912600), sp.Rational(395, 3672), sp.Rational(785, 2704), sp.Rational(3, 50)]]
, 5)
# Step 2.a.xii: RK5 Dormand-Prince Method Alternative
Butcher_dict['DP5alt'] = (
[[0],
[sp.Rational(1,10), sp.Rational(1,10)],
[sp.Rational(2,9), sp.Rational(-2, 81), sp.Rational(20, 81)],
[sp.Rational(3,7), sp.Rational(615, 1372), sp.Rational(-270, 343), sp.Rational(1053, 1372)],
[sp.Rational(3,5), sp.Rational(3243, 5500), sp.Rational(-54, 55), sp.Rational(50949, 71500), sp.Rational(4998, 17875)],
[sp.Rational(4, 5), sp.Rational(-26492, 37125), sp.Rational(72, 55), sp.Rational(2808, 23375), sp.Rational(-24206, 37125), sp.Rational(338, 459)],
[sp.sympify(1), sp.Rational(5561, 2376), sp.Rational(-35, 11), sp.Rational(-24117, 31603), sp.Rational(899983, 200772), sp.Rational(-5225, 1836), sp.Rational(3925, 4056)],
["", sp.Rational(821, 10800), sp.sympify(0), sp.Rational(19683, 71825), sp.Rational(175273, 912600), sp.Rational(395, 3672), sp.Rational(785, 2704), sp.Rational(3, 50)]]
, 5)
# Step 2.a.xiii: RK5 Cash-Karp Method
Butcher_dict['CK5'] = (
[[0],
[sp.Rational(1,5), sp.Rational(1,5)],
[sp.Rational(3,10),sp.Rational(3,40), sp.Rational(9,40)],
[sp.Rational(3,5), sp.Rational(3,10), sp.Rational(-9,10), sp.Rational(6,5)],
[sp.sympify(1), sp.Rational(-11,54), sp.Rational(5,2), sp.Rational(-70,27), sp.Rational(35,27)],
[sp.Rational(7,8), sp.Rational(1631,55296), sp.Rational(175,512), sp.Rational(575,13824), sp.Rational(44275,110592), sp.Rational(253,4096)],
["",sp.Rational(37,378), sp.sympify(0), sp.Rational(250,621), sp.Rational(125,594), sp.sympify(0), sp.Rational(512,1771)]]
, 5)
# Step 2.a.xiv: RK6 Dormand-Prince Method
Butcher_dict['DP6'] = (
[[0],
[sp.Rational(1,10), sp.Rational(1,10)],
[sp.Rational(2,9), sp.Rational(-2, 81), sp.Rational(20, 81)],
[sp.Rational(3,7), sp.Rational(615, 1372), sp.Rational(-270, 343), sp.Rational(1053, 1372)],
[sp.Rational(3,5), sp.Rational(3243, 5500), sp.Rational(-54, 55), sp.Rational(50949, 71500), sp.Rational(4998, 17875)],
[sp.Rational(4, 5), sp.Rational(-26492, 37125), sp.Rational(72, 55), sp.Rational(2808, 23375), sp.Rational(-24206, 37125), sp.Rational(338, 459)],
[sp.sympify(1), sp.Rational(5561, 2376), sp.Rational(-35, 11), sp.Rational(-24117, 31603), sp.Rational(899983, 200772), sp.Rational(-5225, 1836), sp.Rational(3925, 4056)],
[sp.sympify(1), sp.Rational(465467, 266112), sp.Rational(-2945, 1232), sp.Rational(-5610201, 14158144), sp.Rational(10513573, 3212352), sp.Rational(-424325, 205632), sp.Rational(376225, 454272), sp.sympify(0)],
["", sp.Rational(61, 864), sp.sympify(0), sp.Rational(98415, 321776), sp.Rational(16807, 146016), sp.Rational(1375, 7344), sp.Rational(1375, 5408), sp.Rational(-37, 1120), sp.Rational(1,10)]]
, 6)
# Step 2.a.xv: RK6 Luther's Method
q = sp.sqrt(21)
Butcher_dict['L6'] = (
[[0],
[sp.sympify(1), sp.sympify(1)],
[sp.Rational(1,2), sp.Rational(3,8), sp.Rational(1,8)],
[sp.Rational(2,3), sp.Rational(8,27), sp.Rational(2,27), sp.Rational(8,27)],
[(7 - q)/14, (-21 + 9*q)/392, (-56 + 8*q)/392, (336 -48*q)/392, (-63 + 3*q)/392],
[(7 + q)/14, (-1155 - 255*q)/1960, (-280 - 40*q)/1960, (-320*q)/1960, (63 + 363*q)/1960, (2352 + 392*q)/1960],
[sp.sympify(1), ( 330 + 105*q)/180, sp.Rational(2,3), (-200 + 280*q)/180, (126 - 189*q)/180, (-686 - 126*q)/180, (490 - 70*q)/180],
["", sp.Rational(1, 20), sp.sympify(0), sp.Rational(16, 45), sp.sympify(0), sp.Rational(49, 180), sp.Rational(49, 180), sp.Rational(1, 20)]]
, 6)
# Step 2.a.xvi: RK8 Dormand-Prince Method
Butcher_dict['DP8']=(
[[0],
[sp.Rational(1, 18), sp.Rational(1, 18)],
[sp.Rational(1, 12), sp.Rational(1, 48), sp.Rational(1, 16)],
[sp.Rational(1, 8), sp.Rational(1, 32), sp.sympify(0), sp.Rational(3, 32)],
[sp.Rational(5, 16), sp.Rational(5, 16), sp.sympify(0), sp.Rational(-75, 64), sp.Rational(75, 64)],
[sp.Rational(3, 8), sp.Rational(3, 80), sp.sympify(0), sp.sympify(0), sp.Rational(3, 16), sp.Rational(3, 20)],
[sp.Rational(59, 400), sp.Rational(29443841, 614563906), sp.sympify(0), sp.sympify(0), sp.Rational(77736538, 692538347), sp.Rational(-28693883, 1125000000), sp.Rational(23124283, 1800000000)],
[sp.Rational(93, 200), sp.Rational(16016141, 946692911), sp.sympify(0), sp.sympify(0), sp.Rational(61564180, 158732637), sp.Rational(22789713, 633445777), sp.Rational(545815736, 2771057229), sp.Rational(-180193667, 1043307555)],
[sp.Rational(5490023248, 9719169821), sp.Rational(39632708, 573591083), sp.sympify(0), sp.sympify(0), sp.Rational(-433636366, 683701615), sp.Rational(-421739975, 2616292301), sp.Rational(100302831, 723423059), sp.Rational(790204164, 839813087), sp.Rational(800635310, 3783071287)],
[sp.Rational(13, 20), sp.Rational(246121993, 1340847787), sp.sympify(0), sp.sympify(0), sp.Rational(-37695042795, 15268766246), sp.Rational(-309121744, 1061227803), sp.Rational(-12992083, 490766935), sp.Rational(6005943493, 2108947869), sp.Rational(393006217, 1396673457), sp.Rational(123872331, 1001029789)],
[sp.Rational(1201146811, 1299019798), sp.Rational(-1028468189, 846180014), sp.sympify(0), sp.sympify(0), sp.Rational(8478235783, 508512852), sp.Rational(1311729495, 1432422823), sp.Rational(-10304129995, 1701304382), sp.Rational(-48777925059, 3047939560), sp.Rational(15336726248, 1032824649), sp.Rational(-45442868181, 3398467696), sp.Rational(3065993473, 597172653)],
[sp.sympify(1), sp.Rational(185892177, 718116043), sp.sympify(0), sp.sympify(0), sp.Rational(-3185094517, 667107341), sp.Rational(-477755414, 1098053517), sp.Rational(-703635378, 230739211), sp.Rational(5731566787, 1027545527), sp.Rational(5232866602, 850066563), sp.Rational(-4093664535, 808688257), sp.Rational(3962137247, 1805957418), sp.Rational(65686358, 487910083)],
[sp.sympify(1), sp.Rational(403863854, 491063109), sp.sympify(0), sp.sympify(0), sp.Rational(-5068492393, 434740067), sp.Rational(-411421997, 543043805), sp.Rational(652783627, 914296604), sp.Rational(11173962825, 925320556), sp.Rational(-13158990841, 6184727034), sp.Rational(3936647629, 1978049680), sp.Rational(-160528059, 685178525), sp.Rational(248638103, 1413531060), sp.sympify(0)],
["", sp.Rational(14005451, 335480064), sp.sympify(0), sp.sympify(0), sp.sympify(0), sp.sympify(0), sp.Rational(-59238493, 1068277825), sp.Rational(181606767, 758867731), sp.Rational(561292985, 797845732), sp.Rational(-1041891430, 1371343529), sp.Rational(760417239, 1151165299), sp.Rational(118820643, 751138087), sp.Rational(-528747749, 2220607170), sp.Rational(1, 4)]]
, 8)
# As documented in the NRPy+ tutorial module
# Tutorial-RK_Butcher_Table_Validation.ipynb ,
# this module will validate the Python dictionary
# of Butcher tables.
# Authors: Brandon Clark
# Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Initialize needed Python/NRPy+ modules
import sympy as sp
import NRPy_param_funcs as par
import numpy as np
from MoLtimestepping.RK_Butcher_Table_Dictionary import Butcher_dict
import os
import sys
from IPython.display import Image, display
# Step 2a: Defining the right-hand side of the ODE
rhs_dict = {}
def fypt(y,t):
return y+t
rhs_dict['ypt'] = fypt
def fy(y,t):
return y
rhs_dict['y'] = fy
def feypt(y,t):
return sp.exp(1.0*(y+t))
rhs_dict['eypt'] = feypt
def ftpoly6(y,t):
return 2*t**6-389*t**5+15*t**4-22*t**3+81*t**2-t+42
rhs_dict['tpoly6'] = ftpoly6
def ftpoly5(y,t):
return t**5 + t**4 + t**3 + t**2 + t + 1
rhs_dict["tpoly5"] = ftpoly5
def fty2pt(y,t):
return t*y**2+t
rhs_dict["ty2pt"] = fty2pt
def fymcost(y,t):
return y-sp.cos(t)
rhs_dict["ymcost"] = fymcost
def fypsint(y,t):
return y+sp.sin(t)
rhs_dict['ypsint'] = fypsint
# Step 2b: Defining a Validation Function
def Validate(Butcher_dict, Butcher_key, yn, tn, rhs_key):
# Set needed symbolic expressions
t, dt = sp.symbols('t dt')
# 1. First we solve the ODE exactly
y = sp.Function('y')
sol = sp.dsolve(sp.Eq(y(t).diff(t), rhs_dict[rhs_key](y(t), t)), y(t)).rhs
constants = sp.solve([sol.subs(t,tn)-yn])
exact = sol.subs(constants)
# 2. Now we solve the ODE numerically using specified Butcher table
# Access the requested Butcher table
Butcher = Butcher_dict[Butcher_key][0]
# Determine number of predictor-corrector steps
L = len(Butcher)-1
# Set a temporary array for update values
k = np.zeros(L, dtype=object)
# Initialize intermediate variable
yhat = 0
# Initialize the updated solution
ynp1 = 0
for i in range(L):
#Initialize and approximate update for solution
yhat = yn
for j in range(i):
# Update yhat for solution using a_ij Butcher table coefficients
yhat += Butcher[i][j+1]*k[j]
if Butcher_key == "DP8" or Butcher_key == "L6":
yhat = 1.0*sp.N(yhat,20) # Otherwise the adding of fractions kills performance.
# Determine the next corrector variable k_i using c_i Butcher table coefficients
k[i] = dt*rhs_dict[rhs_key](yhat, tn + Butcher[i][0]*dt)
# Update the solution at the next iteration ynp1 using Butcher table coefficients
ynp1 += Butcher[L][i+1]*k[i]
# Finish determining the solution for the next iteration
ynp1 += yn
# Determine the order of the RK method
order = Butcher_dict[Butcher_key][1]+2
# Produces Taylor series of exact solution at t=tn about t = 0 with the specified order
exact_series = sp.series(exact.subs(t, dt),dt, 0, order)
num_series = sp.series(ynp1, dt, 0, order)
diff = exact_series-num_series
return diff
# # Step 2d: Validating Convergence with ScalarWave PDE in Cartesian Coordinates
# def fd_order(RK_order):
# if (RK_order+1)%2==0: # If RK_order is odd, then set FD_order (must be even) to RK_order+1
# return RK_order+1
# else:
# return RK_order+2 # If RK_order is even, then set FD_order (must be even) to RK_order+2
# # CFL_FACTOR = 0.5
# for key,value in Butcher_dict.items():
# for CFL_FACTOR in [0.5]:
# with open("ScalarWave/params.txt", "w") as file:
# # DON'T ADD SPACES TO THE FOLLOWING!
# file.write(
# """RK_method="""+str(key)+"""
# FD_order="""+str(fd_order(value[1]))+"""
# REAL=double
# CFL_FACTOR="""+str(CFL_FACTOR))
# outfilename = "Out"+str(key)+"_CFL_"+str(CFL_FACTOR)+".png"
# os.system("rm -f "+outfilename)
# os.system("jupyter nbconvert --to notebook --inplace --execute --ExecutePreprocessor.timeout=-1 Tutorial-Start_to_Finish-ScalarWave.ipynb")
# if os.path.isfile(outfilename):
# print("Successfully generated figure "+outfilename)
# #display(Image(filename=outfilename,width=600))
# else:
# print("ERROR: Failed to generate figure "+outfilename)
# sys.exit(1)
# As documented in the NRPy+ tutorial module
# Tutorial-Coutput__Parameter_Interface.ipynb
# this core NRPy+ module is used for
# initializing, storing, and recalling
# parameters.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # Import SymPy
import os, sys # Standard Python: OS-independent system functions
from collections import namedtuple # Standard Python: Enable namedtuple data type
glb_params_list = [] # = where we store NRPy+ parameters and default values of parameters. A list of named tuples
glb_paramsvals_list = [] # = where we store NRPy+ parameter values.
glb_param = namedtuple('glb_param', 'type module parname defaultval')
glb_Cparams_list = [] # = where we store C runtime parameters and default values of parameters. A list of named tuples
glb_Cparam = namedtuple('glb_Cparam','type module parname defaultval')
veryverbose = False
def initialize_param(input):
if get_params_idx(input) == -1:
glb_params_list.append(input)
glb_paramsvals_list.append(input.defaultval)
else:
if veryverbose == True:
print("initialize_param() minor warning: Did nothing; already initialized parameter "+input.module+"::"+input.parname)
def initialize_Cparam(input):
if get_params_idx(input,Cparam=True) == -1:
glb_Cparams_list.append(input)
else:
if veryverbose == True:
print("initialize_Cparam() minor warning: Did nothing; already initialized parameter "+input.module+"::"+input.parname)
# Given the named tuple `input` and list of named tuples `params`,
# defined according to namedtuple('param', 'type module name defaultval'),
# where in the case of `input`, defaultval need not be set,
# return the list index of `params` that matches `input`.
# On error returns -1
def get_params_idx(input,Cparam=False):
# inspired by: https://stackoverflow.com/questions/2917372/how-to-search-a-list-of-tuples-in-python:
if Cparam==False:
list = [i for i, v in enumerate(glb_params_list)
if (input.type=="ignoretype" or input.type==v[0]) and input.module == v[1] and input.parname == v[2]]
else:
list = [i for i, v in enumerate(glb_Cparams_list) if input.parname == v[2]]
if list == []:
return -1 # No match found => error out!
else:
if len(list) > 1:
print("Error: Found multiple parameters matching "+str(input))
sys.exit(1)
return list.pop() # pop() returns the index
def get_params_value(input):
idx = get_params_idx(input)
if idx < 0:
print("Error: could not find a parameter matching:",input)
print("Full list of modules:\n",glb_params_list)
sys.exit(1)
else:
return glb_paramsvals_list[idx]
#
def idx_from_str(varname,modname=""):
if "::" in varname:
splitstring = re.split('::', varname)
modname=splitstring[0]
varname=splitstring[1]
# inspired by: https://stackoverflow.com/questions/2917372/how-to-search-a-list-of-tuples-in-python:
if modname == "":
list = [i for i, v in enumerate(glb_params_list) if v[2] == varname]
else:
list = [i for i, v in enumerate(glb_params_list) if (v[1] == modname and v[2] == varname)]
if list == []:
print("Error: Could not find a parameter matching \""+varname+"\" in ",glb_params_list)
sys.exit(1)
if len(list) > 1:
print("Error: Found more than one parameter named \""+varname+"\". Use get_params_value() instead.")
sys.exit(1)
return list.pop()
def parval_from_str(string):
return glb_paramsvals_list[idx_from_str(string)]
def set_parval_from_str(string,value):
glb_paramsvals_list[idx_from_str(string)] = value
# parse_param_string__set__params_and_paramsvars:
# Summary: This function parses a string like this
# module::variablename=value
# into its component parts: module,variablename,value
# then hunts for module,variablename in the
# params list. When it finds the matching index in the
# list "idx", it sets paramsvals[idx] = value.
# Usage comment: This function parses both parameter file
# inputs as well as parameter inputs from the command
# line. You should set filename = "" if reading from the
# command line. This ensures the error messages are
# appropriate for the context.
import re
def set_paramsvals_value(line,filename="", FindMainModuleMode=False):
MainModuleFound = True
if FindMainModuleMode == True:
MainModuleFound = False
# First remove carriage return and leading whitespace:
stripped_line_of_text = line.strip()
# Only process lines that do NOT start with a hash
if not stripped_line_of_text.startswith("#"):
# Valid lines take the form:
# module::variable = value # Comment here
# Thus, using the delimiters "::", "=", and "#",
# with the regex split command, the first three
# items in single_param_def will be [module, variablename, value]
single_param_def = re.split('::|=|#', stripped_line_of_text)
# First verify that single_param_def has at least 3 parts:
if len(single_param_def) < 3:
if filename != "":
print("Error: the line " + line + " in parameter file " + filename + " is not in the form")
print("\"module::variable = value\"")
else:
print("Error: the command-line argument " + stripped_line_of_text + " is not in the form")
print("\"module::variable=value\" <-- NOTICE NO SPACES ALLOWED!")
sys.exit(1)
# Next remove all leading/trailing whitespace from single_param_def
for i in range(len(single_param_def)):
single_param_def[i] = single_param_def[i].strip()
if FindMainModuleMode == False:
# Next find the parameter in the params list and set
# the corresponding element in paramsvals to the value.
idx = get_params_idx(glb_param("ignoretype", single_param_def[0], single_param_def[1], "ignoredefval"))
# If parameter is not found, print useful error message, then exit:
if idx == -1:
if filename != "":
print("Error: when reading line \"" + line + "\" in parameter file \"" + filename + "\":")
else:
print("Error: when parsing command-line argument \"" + stripped_line_of_text + "\":")
print("\t\tcould not find parameter \""+ single_param_def[1] + "\" in \""+single_param_def[0]+"\" module.")
sys.exit(1)
# If parameter is found at index idx, set paramsval[idx] to the value specified in the file.
partype = glb_params_list[idx].type
if partype == "bool":
if single_param_def[2] == "True":
glb_paramsvals_list[idx] = True
elif single_param_def[2] == "False":
glb_paramsvals_list[idx] = False
else:
print("Error: \"bool\" type can only take values of \"True\" or \"False\"")
sys.exit(1)
elif partype == "int":
glb_paramsvals_list[idx] = int(single_param_def[2])
elif partype == "REAL" or \
partype == "char" or \
partype == "char *":
glb_paramsvals_list[idx] = single_param_def[2]
else:
print("Error: type \""+partype+"\" on variable \""+ glb_params_list[idx].parname +"\" is unsupported.")
print("Supported types include: bool, int, REAL, REALARRAY, char, and char *")
sys.exit(1)
elif FindMainModuleMode == True and MainModuleFound == False:
if single_param_def[0] == "NRPy" and single_param_def[1] == "MainModule":
idx = get_params_idx(glb_param("ignoretype", single_param_def[0], single_param_def[1], "ignoredefval"))
if idx == -1:
print("Critical error: NRPy::MainModule is uninitialized!")
sys.exit(1)
glb_paramsvals_list[idx] = single_param_def[2]
def Cparameters(type,module,names,defaultvals,assumption="Real"):
output = []
# if names is not a list, make it a list, to
# simplify the remainder of this routine.
if not isinstance(names,list):
names = [names]
defaultval_list = []
if not isinstance(defaultvals,list):
for i in range(len(names)):
defaultval_list.append(defaultvals)
else:
# If defaultvals *is* a list, then make sure it has the same number of elements as "names".
if len(defaultvals) != len(names):
print("Error in Cparameters(): Was provided a list of variables:\n"+str(names)+"\n")
print("and a list of their default values:\n"+str(defaultvals)+"\n")
print("but the lists have different lengths ("+str(len(names))+" != "+str(len(defaultvals))+")\n")
sys.exit(1)
defaultval_list = defaultvals
for i in range(len(names)):
initialize_Cparam(glb_Cparam(type, module, names[i], defaultval_list[i]))
if assumption == "Real":
tmp = sp.Symbol(names[i], real=True) # Assumes all Cparameters are real.
elif assumption == "RealPositive":
tmp = sp.Symbol(names[i], real=True, positive=True) # Assumes all Cparameters are real and positive.
else:
print("Error: assumption "+str(assumption)+" not supported.")
sys.exit(1)
output.append(tmp)
if len(names) == 1:
return output[0]
return output
def generate_Cparameters_Ccodes(directory="./"):
# Step 1: Check that Cparams types are supported.
for i in range(len(glb_Cparams_list)):
partype = glb_Cparams_list[i].type
if partype != "bool" and \
partype != "#define" and \
partype != "char" and \
partype != "int" and \
partype != "REAL":
print("Error: parameter "+glb_Cparams_list[i].module+"::"+glb_Cparams_list[i].parname+" has unsupported type: \""
+ glb_Cparams_list[i].type + "\"")
sys.exit(1)
# Step 2: Generate C code to declare C paramstruct;
# output to "declare_Cparameters_struct.h"
with open(os.path.join(directory,"declare_Cparameters_struct.h"), "w") as file:
file.write("typedef struct __paramstruct__ {\n")
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type != "#define":
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
file.write(Ctype + " " + glb_Cparams_list[i].parname + ";\n")
file.write("} paramstruct;\n")
# Step 3: Generate C code to set all elements in
# C paramstruct to default values; output to
# "set_Cparameters_default.h"
with open(os.path.join(directory,"set_Cparameters_default.h"), "w") as file:
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type != "#define":
Coutput = "params." + glb_Cparams_list[i].parname
if isinstance(glb_Cparams_list[i].defaultval, (bool,int,float)):
Coutput += " = " + str(glb_Cparams_list[i].defaultval).lower() + ";\n"
elif glb_Cparams_list[i].type == "char" and isinstance(glb_Cparams_list[i].defaultval, (str)):
Coutput += " = \"" + str(glb_Cparams_list[i].defaultval).lower() + "\";\n"
else:
Coutput += " = " + str(glb_Cparams_list[i].defaultval) + ";\n"
file.write(Coutput)
# Step 4: Generate C code to set C parameter constants
# (i.e., all ints != -12345678 and REALs != 1e300);
# output to filename "set_Cparameters.h" if SIMD_enable==False
# or "set_Cparameters-SIMD.h" if SIMD_enable==True
# Step 4.a: Output non-SIMD version, set_Cparameters.h
def gen_set_Cparameters(pointerEnable=True):
returnstring = ""
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
pointer = "->"
if pointerEnable==False:
pointer = "."
if not ((Ctype == "REAL" and glb_Cparams_list[i].defaultval == 1e300) or Ctype == "#define"):
Coutput = "const "+Ctype+" "+glb_Cparams_list[i].parname+" = "+"params"+pointer+glb_Cparams_list[i].parname + ";\n"
returnstring += Coutput
return returnstring
with open(os.path.join(directory,"set_Cparameters.h"), "w") as file:
file.write(gen_set_Cparameters(pointerEnable=True))
with open(os.path.join(directory,"set_Cparameters-nopointer.h"), "w") as file:
file.write(gen_set_Cparameters(pointerEnable=False))
# Step 4.b: Output SIMD version, set_Cparameters-SIMD.h
with open(os.path.join(directory,"set_Cparameters-SIMD.h"), "w") as file:
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
parname = glb_Cparams_list[i].parname
if Ctype == "REAL" and glb_Cparams_list[i].defaultval != 1e300:
Coutput = "const REAL NOSIMD" + parname + " = " + "params->" + glb_Cparams_list[i].parname + ";\n"
Coutput += "const REAL_SIMD_ARRAY " + parname + " = ConstSIMD(NOSIMD" + parname + ");\n"
file.write(Coutput)
elif glb_Cparams_list[i].defaultval != 1e300 and Ctype !="#define":
Coutput = "const "+Ctype+" "+parname + " = " + "params->" + glb_Cparams_list[i].parname + ";\n"
file.write(Coutput)
from sympy import Integer,Symbol,symbols,simplify,Rational,sign,Function,srepr,sin,cos,exp,log,Abs,Add,Mul,Pow,preorder_traversal,N,Float,S,var,sympify
import re, sys
# For debugging purposes, Part 1:
# Basic arithmetic operations
def ConstSIMD_check(a):
return Float(a,34)
def AbsSIMD_check(a):
return Abs(a)
def nrpyAbsSIMD_check(a):
return Abs(a)
def AddSIMD_check(a,b):
return a+b
def SubSIMD_check(a,b):
return a-b
def MulSIMD_check(a,b):
return a*b
def FusedMulAddSIMD_check(a,b,c):
return a*b + c
def FusedMulSubSIMD_check(a,b,c):
return a*b - c
def DivSIMD_check(a,b):
return a/b
def signSIMD_check(a):
return sign(a)
# For debugging purposes, Part 2:
# Transcendental operations
def PowSIMD_check(a, b):
return a**b
def SqrtSIMD_check(a):
return a**(Rational(1,2))
def CbrtSIMD_check(a):
return a**(Rational(1,3))
def ExpSIMD_check(a):
return exp(a)
def LogSIMD_check(a):
return log(a)
def SinSIMD_check(a):
return sin(a)
def CosSIMD_check(a):
return cos(a)
# Input: SymPy expression.
# Return value: SymPy expression containing all needed SIMD compiler intrinsics
# Complication: SIMD functions require numerical constants to be stored in SIMD arrays
# Resolution: This function extends lists "SIMD_const_varnms" and "SIMD_const_values",
# which store the name of each constant SIMD array (e.g., _Integer_1) and
# the value of each variable (e.g., 1.0).
def expr_convert_to_SIMD_intrins(expr, SIMD_const_varnms,SIMD_const_values,SIMD_const_suffix="",debug="False"):
# Declare all variables, so we can eval them in the next (AddSIMD & MulSIMD) step
for item in preorder_traversal(expr):
for i in range(len(item.args)):
if isinstance(item.args[i],Symbol):
var(str(item.args[i]))
expr_orig = expr
AbsSIMD = Function("AbsSIMD")
AddSIMD = Function("AddSIMD")
SubSIMD = Function("SubSIMD")
MulSIMD = Function("MulSIMD")
FusedMulAddSIMD = Function("FusedMulAddSIMD")
FusedMulSubSIMD = Function("FusedMulSubSIMD")
DivSIMD = Function("DivSIMD")
SignSIMD = Function("SignSIMD")
PowSIMD = Function("PowSIMD")
SqrtSIMD = Function("SqrtSIMD")
CbrtSIMD = Function("CbrtSIMD")
ExpSIMD = Function("ExpSIMD")
LogSIMD = Function("LogSIMD")
SinSIMD = Function("SinSIMD")
CosSIMD = Function("CosSIMD")
# Step 1: Replace transcendental, power, and division functions with SIMD equivalents
# Note that due to how SymPy expresses rational numbers, the following does not
# affect fractional expressions of integers
for item in preorder_traversal(expr):
if item.func == Abs:
expr = expr.xreplace({item: AbsSIMD(item.args[0])})
elif item.func == exp:
expr = expr.xreplace({item: ExpSIMD(item.args[0])})
elif item.func == log:
expr = expr.xreplace({item: LogSIMD(item.args[0])})
elif item.func == sin:
expr = expr.xreplace({item: SinSIMD(item.args[0])})
elif item.func == cos:
expr = expr.xreplace({item: CosSIMD(item.args[0])})
elif item.func == sign:
expr = expr.xreplace({item: SignSIMD(item.args[0])})
# Fun little recursive function for constructing integer powers:
def IntegerPowSIMD(a, n):
if n == 2:
return MulSIMD(a, a)
elif n > 2:
return MulSIMD(IntegerPowSIMD(a, n - 1), a)
elif n <= -2:
return DivSIMD(1, IntegerPowSIMD(a, -n))
elif n == -1:
return DivSIMD(1, a)
for item in preorder_traversal(expr):
if item.func == Pow:
if item.args[1] == 0.5:
expr = expr.xreplace({item: SqrtSIMD(item.args[0])})
elif item.args[1] == -0.5:
expr = expr.xreplace({item: DivSIMD(1,SqrtSIMD(item.args[0]))})
elif item.args[1] == Rational(1,3):
expr = expr.xreplace({item: CbrtSIMD(item.args[0])})
elif item.args[1] == int(item.args[1]):
expr = expr.xreplace({item: IntegerPowSIMD(item.args[0], item.args[1])})
else:
expr = expr.xreplace({item: PowSIMD(item.args[0], item.args[1])})
# Step 2: Replace all rational numbers (expressed as Rational(a,b))
# and integers with the new functions RationalTMP and
# IntegerTMP, where Rational(a,b) -> RationalTMP(a,b)
# and Integer(a) -> IntegerTMP(a)
RationalTMP = Function("RationalTMP")
IntegerTMP = Function("IntegerTMP")
string = str(srepr(expr))
string2 = re.sub(r'Integer\(([+\-0-9]+)\)',
"(Function('IntegerTMP')('\\1'))", string)
expr = eval(string2)
string = str(srepr(expr))
string2 = re.sub(r'Rational\(([-0-9]+), ([0-9]+)\)',
"(Function('RationalTMP')(('\\1'),('\\2')))", string)
expr = eval(string2)
# Step 3: The pattern Mul(-1,Rational(a,b)) is often seen. Replace with Rational(-a,b).
for item in preorder_traversal(expr):
if item.func == Mul:
idx_has_Negative1 = -10
for i in range(len(item.args)):
if item.args[i].func == IntegerTMP and item.args[i].args[0] == -1:
idx_has_Negative1 = i
if idx_has_Negative1 >= 0:
for i in range(len(item.args)):
if item.args[i].func==RationalTMP:
tempitem_orig = Mul(IntegerTMP(-1),RationalTMP(item.args[i].args[0], item.args[i].args[1]))
tempitem_new = RationalTMP(-item.args[i].args[0], item.args[i].args[1])
expr = expr.subs(tempitem_orig, tempitem_new )
break
# Step 4: SIMD multiplication and addition compiler intrinsics read in
# only two arguments at once, where SymPy's Mul() and Add()
# operators can read an arbitrary number of arguments.
# Here, we split e.g., Mul(a,b,c,d) into
# MulSIMD(a,MulSIMD(b,MulSIMD(c,d))),
# To accomplish this easily, we construct a string
# 'MulSIMD(A,MulSIMD(B,...', where MulSIMD(a,b) is some user-
# defined function that takes in only two arguments, and then
# evaluate the string using the eval() function.
# Implementation detail: If we did not perform Step 2 above, the eval
# function would automatically evaluate all Rational expressions
# as though they were input as integers: e.g., 1/2 evaluates to 0.
# This is undesirable, so we instead define new, temporary
# functions IntegerTMP and RationalTMP that are undisturbed by
# the eval()
for item in preorder_traversal(expr):
if item.func == Mul:
blahtmp = symbols('blahtmp')
tempitem = blahtmp
for i in range(len(item.args)-1):
tempitem = MulSIMD(item.args[i],tempitem)
tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]})
expr = expr.xreplace({item: tempitem})
for item in preorder_traversal(expr):
if item.func == Add:
blahtmp = symbols('blahtmp')
tempitem = blahtmp
for i in range(len(item.args)-1):
tempitem = AddSIMD(item.args[i],tempitem)
tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]})
expr = expr.xreplace({item: tempitem})
# Step 5: Simplification patterns:
# Step 5a: Replace the pattern Mul(Div(1,b),a) or Mul(a,Div(1,b)) with Div(a,b):
for item in preorder_traversal(expr):
if item.func == MulSIMD:
if item.func == MulSIMD and (item.args[0].func == DivSIMD and item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == 1):
expr = expr.xreplace({item: DivSIMD(item.args[1],item.args[0].args[1])})
if item.func == MulSIMD and (item.args[1].func == DivSIMD and item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == 1):
expr = expr.xreplace({item: DivSIMD(item.args[0],item.args[1].args[1])})
# Step 5: Subtraction intrinsics. SymPy replaces all a-b with a + (-b) = Add(a,Mul(-1,b))
# Here, we replace
# a) AddSIMD(a,MulSIMD(-1,b)),
# b) AddSIMD(a,MulSIMD(b,-1)),
# c) AddSIMD(MulSIMD(-1,b),a), and
# d) AddSIMD(MulSIMD(b,-1),a)
# with SubSIMD(a,b)
for item in preorder_traversal(expr):
tempitem = item
# First match patterns a) and b): AddSIMD(a,MULSIMD(.,.)):
if item.func == AddSIMD and item.args[1].func == MulSIMD:
# Pattern a) AddSIMD(a,MulSIMD(-1,b)) --> SubSIMD(a,b):
if item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == -1:
tempitem = SubSIMD(item.args[0],item.args[1].args[1])
# Pattern b) AddSIMD(a,MulSIMD(b,-1)) --> SubSIMD(a,b):
elif item.args[1].args[1].func == IntegerTMP and item.args[1].args[1].args[0] == -1:
tempitem = SubSIMD(item.args[0],item.args[1].args[0])
# Next match patterns c) and d): AddSIMD(MulSIMD(.,.),a):
elif item.func == AddSIMD and item.args[0].func == MulSIMD:
# Pattern c) AddSIMD(MulSIMD(-1,b),a) --> SubSIMD(a,b):
if item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == -1:
tempitem = SubSIMD(item.args[1],item.args[0].args[1])
# Pattern d) AddSIMD(MulSIMD(b,-1,a)) --> SubSIMD(a,b):
elif item.args[0].args[1].func == IntegerTMP and item.args[0].args[1].args[0] == -1:
tempitem = SubSIMD(item.args[1],item.args[0].args[0])
expr = expr.subs(item,tempitem)
# Step 6: Now that all multiplication and addition functions only take two
# arguments, we can now easily define fused-multiply-add functions,
# where AddSIMD(a,MulSIMD(b,c)) = b*c + a = FusedMulAddSIMD(b,c,a),
# or AddSIMD(MulSIMD(b,c),a) = b*c + a = FusedMulAddSIMD(b,c,a).
# Fused multiply add (FMA3) is standard on Intel CPUs with the AVX2
# instruction set, starting with Haswell processors in 2013:
# https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
for item in preorder_traversal(expr):
tempitem = item
# If the pattern is a*b + c, replace with FMA(a,b,c)
if item.func == AddSIMD and item.args[0].func == MulSIMD:
tempitem = FusedMulAddSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1])
# If the pattern is c + a*b, replace with FMA(a,b,c)
if item.func == AddSIMD and item.args[1].func == MulSIMD:
tempitem = FusedMulAddSIMD(item.args[1].args[0],item.args[1].args[1],item.args[0])
# If the pattern is a*b - c, replace with FMS(a,b,c)
if item.func == SubSIMD and item.args[0].func == MulSIMD:
tempitem = FusedMulSubSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1])
if item != tempitem: expr = expr.subs(item, tempitem)
# Step 7: SIMD intrinsics cannot take integers or rational numbers as arguments.
# Therefore we must declare all integers & rational numbers as
# const vector doubles (e.g., const _m256d ...). To make the code
# more human readable, we adopt the convention
# RationalTMP(1,3) = 1/3 = "Rational_1_3
# RationalTMP(-1,3) = -1/3 = "Rational_m1_3
# TODO: Keep track of all integers and rationals, so repeats are not computed?
# Step 7a: Set all variable names and corresponding values.
for item in preorder_traversal(expr):
if item.func == RationalTMP:
# Set variable name
if item.args[0]*item.args[1] < 0:
SIMD_const_varnms.extend(["_Rational_m"+str(abs(item.args[0]))+"_"+str(abs(item.args[1]))+SIMD_const_suffix])
elif item.args[0] > 0 and item.args[1] > 0:
SIMD_const_varnms.extend(["_Rational_"+str(item.args[0])+"_"+str(item.args[1])+SIMD_const_suffix])
else:
# E.g., doesn't make sense to have -1/-3. SymPy should have simplified this.
print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels")
print("Specifically, found that a="+str(item.args[0])+" and b="+str(item.args[1]))
sys.exit(1)
# Set variable value, to 34 digits of precision
SIMD_const_values.extend([str(N(Float(item.args[0],34)/Float(item.args[1],34),34))])
elif item.func == IntegerTMP:
# Set variable name
if item.args[0] < 0:
SIMD_const_varnms.extend(["_Integer_m"+str(-item.args[0])+SIMD_const_suffix])
else:
SIMD_const_varnms.extend(["_Integer_" + str(item.args[0])+SIMD_const_suffix])
# Set variable value, to 34 digits of precision
SIMD_const_values.extend([str((Float(item.args[0],34)))])
# Step 7b: Replace all integers and rationals with the appropriate variable names:
for item in preorder_traversal(expr):
tempitem = item
if item.func == RationalTMP:
if item.args[0]*item.args[1] < 0:
tempitem = var("_Rational_m" + str(abs(item.args[0])) + "_" + str(abs(item.args[1]))+SIMD_const_suffix)
elif item.args[0] > 0 and item.args[1] > 0:
tempitem = var("_Rational_" + str(item.args[0]) + "_" + str(item.args[1])+SIMD_const_suffix)
else:
# E.g., doesn't make sense to have -1/-3. SymPy should have simplified this.
print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels")
print("Specifically, found that a=" + str(item.args[0]) + " and b=" + str(item.args[1]))
sys.exit(1)
elif item.func == IntegerTMP:
if item.args[0] < 0:
tempitem = var("_Integer_m" + str(-item.args[0])+SIMD_const_suffix)
else:
tempitem = var("_Integer_" + str(item.args[0])+SIMD_const_suffix)
if item != tempitem: expr = expr.subs(item, tempitem)
def lookup_name_output_idx(name, list_of_names):
for i in range(len(list_of_names)):
if list_of_names[i] == name:
return i
print("I SHOULDN'T BE HERE!",name,list_of_names)
sys.exit(1)
if debug=="True":
expr_check = expr
if "SIMD" in str(expr):
expr_check = eval(str(expr).replace("SIMD","SIMD_check"))
for item in preorder_traversal(expr_check):
tempitem = item
if item.is_Symbol and str(item)[0]=="_":
if str(item)[:9]=="_Integer_" or str(item)[:10]=="_Rational_":
tempitem = SIMD_const_values[lookup_name_output_idx(str(item), SIMD_const_varnms)]
if item != tempitem: expr_check = expr_check.subs(item, tempitem)
expr_diff = expr_check - expr_orig
# Some variables do not want to cancel in SymPy ~0.7.4. The eval(str(srepr())) below normalizes the expression.
expr_diff = eval(str(srepr(expr_diff)))
for item in preorder_traversal(expr_diff):
if item.func == Float:
if abs(item - Integer(item)) < 1.0e-14:
expr_diff = expr_diff.xreplace({item:Integer(item)})
# Only simplify if expr_diff != 0:
if expr_diff != 0:
simp_expr_diff = simplify(expr_diff)
if simp_expr_diff != 0:
print("Warning: found possible diff",(simp_expr_diff))
return(expr)
# As documented in the NRPy+ tutorial notebook
# Tutorial-cmdline_helper.ipynb, this Python script
# provides a multi-platform means to run executables,
# remove files, and compile code.
# Basic functions:
# check_executable_exists(): Check to see whether an executable exists.
# Error out or return False if not exists;
# return True if executable exists in PATH.
# C_compile(): Compile C code using gcc.
# Execute(): Execute generated executable file, using taskset
# if available. Calls Execute_input_string() to
# redirect output from stdout & stderr to desired
# destinations.
# Execute_input_string(): Executes an input string and redirects
# output from stdout & stderr to desired destinations.
# delete_existing_files(file_or_wildcard):
# Runs del file_or_wildcard in Windows, or
# rm file_or_wildcard in Linux/MacOS
# Authors: Brandon Clark
# Zach Etienne
# zachetie **at** gmail **dot* com
# Kevin Lituchy
import io, os, shlex, subprocess, sys, time, multiprocessing, getpass, platform
# check_executable_exists(): Check to see whether an executable exists.
# Error out or return False if not exists;
# return True if executable exists in PATH.
def check_executable_exists(exec_name,error_if_not_found=True):
cmd = "where" if os.name == "nt" else "which"
try:
subprocess.check_output([cmd, exec_name])
except subprocess.CalledProcessError:
if error_if_not_found:
print("Sorry, cannot execute the command: " + exec_name)
sys.exit(1)
else:
return False
return True
# C_compile(): Write a function to compile the Main C code into an executable file
def C_compile(main_C_output_path, main_C_output_file, compile_mode="optimized", custom_compile_string=""):
print("Compiling executable...")
# Step 1: Check for gcc compiler
check_executable_exists("gcc")
# Step 2: Delete existing version of executable
delete_string = ""
if os.name == "nt":
main_C_output_file += ".exe"
delete_existing_files(main_C_output_file)
# Step 3: Compile the executable
if compile_mode=="safe":
compile_string = "gcc -O2 -g -fopenmp "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="icc":
check_executable_exists("icc")
compile_string = "icc -O2 -xHost -qopenmp -unroll "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="custom":
Execute_input_string(custom_compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="optimized":
compile_string = "gcc -Ofast -fopenmp -march=native -funroll-loops "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
# Step 3.A: Revert to more compatible gcc compile option
print("Most optimized compilation failed. Removing -march=native:")
compile_string = "gcc -Ofast -fopenmp -funroll-loops "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
if not os.path.isfile(main_C_output_file):
# Step 3.B: Revert to maximally compatible gcc compile option
print("Next-to-most optimized compilation failed. Moving to maximally-compatible gcc compile option:")
compile_string = "gcc -O2 "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Step 3.C: If there are still missing components within the compiler, say compilation failed
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
else:
print("Sorry, compile_mode = \""+compile_mode+"\" unsupported.")
sys.exit(1)
print("Finished compilation.")
# Execute(): Execute generated executable file, using taskset
# if available. Calls Execute_input_string() to
# redirect output from stdout & stderr to desired
# destinations.
def Execute(executable, executable_output_arguments="", file_to_redirect_stdout=os.devnull,verbose=True):
# Step 1: Delete old version of executable file
if file_to_redirect_stdout != os.devnull:
delete_existing_files(file_to_redirect_stdout)
# Step 2: Build the script for executing the desired executable
execute_string = ""
# When in Windows...
# https://stackoverflow.com/questions/1325581/how-do-i-check-if-im-running-on-windows-in-python
if os.name == "nt":
# ... do as the Windows do
# https://stackoverflow.com/questions/49018413/filenotfounderror-subprocess-popendir-windows-7
execute_prefix = "cmd /c " # Run with cmd /c executable [options] on Windows
else:
execute_prefix = "./" # Run with ./executable [options] on Linux & Mac
taskset_exists = check_executable_exists("taskset", error_if_not_found=False)
if taskset_exists:
execute_string += "taskset -c 0"
if getpass.getuser() != "jovyan": # on mybinder, username is jovyan, and taskset -c 0 is the fastest option.
# If not on mybinder and taskset exists:
has_HT_cores = False # Does CPU have hyperthreading cores?
if platform.processor() != '': # If processor string returns null, then assume CPU does not support hyperthreading.
# This will yield correct behavior on ARM (e.g., cell phone) CPUs.
has_HT_cores=True
if has_HT_cores == True:
# NOTE: You will observe a speed-up by using only *PHYSICAL* (as opposed to logical/hyperthreading) cores:
N_cores_to_use = int(multiprocessing.cpu_count()/2) # To account for hyperthreading cores
else:
N_cores_to_use = int(multiprocessing.cpu_count()) # Use all cores if none are hyperthreading cores.
# This will happen on ARM (e.g., cellphone) CPUs
for i in range(N_cores_to_use-1):
execute_string += ","+str(i+1)
execute_string += " "
execute_string += execute_prefix+executable+" "+executable_output_arguments
# Step 3: Execute the desired executable
Execute_input_string(execute_string, file_to_redirect_stdout,verbose)
# Execute_input_string(): Executes an input string and redirects
# output from stdout & stderr to desired destinations.
def Execute_input_string(input_string, file_to_redirect_stdout=os.devnull, verbose=True):
if verbose:
print("Executing `"+input_string+"`...")
start = time.time()
# https://docs.python.org/3/library/subprocess.html
if os.name != 'nt':
args = shlex.split(input_string)
else:
args = input_string
# https://stackoverflow.com/questions/18421757/live-output-from-subprocess-command
filename = "tmp.txt"
with io.open(filename, 'w') as writer, io.open(filename, 'rb', buffering=-1) as reader, io.open(file_to_redirect_stdout, 'wb') as rdirect:
process = subprocess.Popen(args, stdout=rdirect, stderr=writer)
while process.poll() is None:
# https://stackoverflow.com/questions/21689365/python-3-typeerror-must-be-str-not-bytes-with-sys-stdout-write/21689447
sys.stdout.write(reader.read().decode('utf-8'))
time.sleep(0.2)
# Read the remaining
sys.stdout.write(reader.read().decode('utf-8'))
delete_existing_files(filename)
end = time.time()
if verbose:
print("Finished executing in "+str(end-start)+" seconds.")
# delete_existing_files(file_or_wildcard):
# Runs del file_or_wildcard in Windows, or
# rm file_or_wildcard in Linux/MacOS
def delete_existing_files(file_or_wildcard):
delete_string = ""
if os.name == "nt":
delete_string += "del " + file_or_wildcard
else:
delete_string += "rm -f " + file_or_wildcard
os.system(delete_string)
# https://stackoverflow.com/questions/1274405/how-to-create-new-folder
def mkdir(newpath):
if not os.path.exists(os.path.join(newpath)):
os.makedirs(os.path.join(newpath))
# finite_difference.py:
# As documented in the NRPy+ tutorial notebook:
# Tutorial-Finite_Difference_Derivatives.ipynb ,
# This module generates C kernels for numerically
# solving PDEs with finite differences.
#
# Depends on: outputC.py and grid.py.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
from outputC import * # NRPy+: Core C code output module
import grid as gri # NRPy+: Functions having to do with numerical grids
import sys # Standard Python module for multiplatform OS-level functions
from operator import itemgetter
# Step 1: Initialize free parameters for this module:
import NRPy_param_funcs as par
modulename = __name__
# Centered finite difference accuracy order
par.initialize_param(par.glb_param("int", modulename, "FD_CENTDERIVS_ORDER", 4))
par.initialize_param(par.glb_param("int", modulename, "FD_KO_ORDER__CENTDERIVS_PLUS", 2))
def FD_outputC(filename,sympyexpr_list, params="", upwindcontrolvec=""):
outCparams = parse_outCparams_string(params)
# Step 0.a:
# In case sympyexpr_list is a single sympy expression,
# convert it to a list with just one element:
if type(sympyexpr_list) is not list:
sympyexpr_list = [sympyexpr_list]
# Step 0.b:
# finite_difference.py takes control over outCparams.includebraces here,
# which is necessary because outputC() is called twice:
# first for the reads from main memory and finite difference
# stencil expressions, and second for the SymPy expressions and
# writes to main memory.
# If outCparams.includebraces==True, then it will close off the braces
# after the finite difference stencil expressions and start new ones
# for the SymPy expressions and writes to main memory, resulting
# in a non-functioning C code.
# To get around this issue, we create braces around the entire
# string of C output from this function, only if
# outCparams.includebraces==True.
# See Step 6 for corresponding end brace.
if outCparams.includebraces == "True":
Coutput = outCparams.preindent+"{\n"
indent = " "
else:
Coutput = ""
indent = ""
# Step 1a:
# Create a list of free symbols in the sympy expr list
# that are registered neither as gridfunctions nor
# as C parameters. These *must* be derivatives,
# so we call the list "list_of_deriv_vars"
list_of_deriv_vars_with_duplicates = []
for expr in sympyexpr_list:
for var in expr.rhs.free_symbols:
vartype = gri.variable_type(var)
if vartype == "other":
# vartype=="other" should ONLY refer to derivatives, so
# if "_dD" or variants do not appear in a variable classified
# neither as a gridfunction nor a Cparameter, then error out.
if ("_dD" in str(var)) or \
("_dKOD" in str(var)) or \
("_dupD" in str(var)) or \
("_ddnD" in str(var)):
pass
else:
print("Error: Unregistered variable \""+str(var)+"\" in SymPy expression for "+expr.lhs)
print("All variables in SymPy expressions passed to FD_outputC() must be registered")
print("in NRPy+ as either a gridfunction or Cparameter, by calling")
print(str(var)+" = register_gridfunctions...() (in ixp/grid) if \""+str(var)+"\" is a gridfunction, or")
print(str(var)+" = Cparameters() (in par) otherwise (e.g., if it is a free parameter set at C runtime).")
sys.exit(1)
list_of_deriv_vars_with_duplicates.append(var)
# elif vartype == "gridfunction":
# list_of_deriv_vars_with_duplicates.append(var)
list_of_deriv_vars = superfast_uniq(list_of_deriv_vars_with_duplicates)
# Upwinding with respect to a control vector: algorithm description.
# To enable, set the FD_outputC()'s fourth function argument to the
# desired control vector. In BSSN, the betaU vector controls the upwinding.
# See https://arxiv.org/pdf/gr-qc/0206072.pdf for motivation and
# https://arxiv.org/pdf/gr-qc/0109032.pdf for implementation details,
# at second order. Note that the BSSN shift vector behaves like a *negative*
# velocity. See http://www.damtp.cam.ac.uk/user/naweb/ii/advection/advection.php
# for a very basic example motivating this choice.
# Step 1b: For each variable with suffix _dupD, append to
# the list_of_deriv_vars the corresponding _ddnD.
# Both are required for control-vector upwinding. See
# the above print() block for further documentation
# on upwinding--both motivation and implementation
# details.
if upwindcontrolvec != "":
for var in list_of_deriv_vars:
if "_dupD" in str(var):
list_of_deriv_vars.append(sp.sympify(str(var).replace("_dupD","_ddnD")))
# Finally, sort the list_of_deriv_vars. This ensures
# consistency in the C code output, and might even be
# tuned to reduce cache misses.
# Thanks to Aaron Meurer for this nice one-liner!
list_of_deriv_vars = sorted(list_of_deriv_vars,key=sp.default_sort_key)
# Step 2:
# Process list_of_deriv_vars into a list of base gridfunctions
# and a list of derivative operators.
# Step 2a:
# First determine the base gridfunction name from
# "list_of_deriv_vars"
deriv__base_gridfunction_name = []
deriv__operator = []
for var in list_of_deriv_vars:
# Step 2a.1: Check that the number of juxtaposed integers
# at the end of a variable name matches the
# number of U's + D's in the variable name:
varstr = str(var)
num_UDs = 0
for i in range(len(varstr)):
if varstr[i] == 'D' or varstr[i] == 'U':
num_UDs += 1
num_digits = 0
i = len(varstr) - 1
while varstr[i].isdigit():
num_digits += 1
i-=1
if num_UDs != num_digits:
print("Error: "+varstr+" has "+str(num_UDs)+" U's and D's, but ")
print(str(num_digits)+" integers at the end. These must be equal.")
print("Please rename your gridfunction.")
sys.exit(1)
# Step 2a.2: Based on the variable name, find the rank of
# the underlying gridfunction of which we're
# trying to take the derivative.
rank = 0 # rank = "number of juxtaposed U's and D's before the underscore in a derivative expression"
underscore_position = -1
for i in range(len(varstr)-1,-1,-1):
if underscore_position > 0 and (varstr[i] == "U" or varstr[i] == "D"):
rank += 1
if varstr[i] == "_":
underscore_position = i
# Step 2a.3: Based on the variable name, find the order
# of the derivative we're trying to take.
deriv_order = 0 # deriv_order = "number of D's after the underscore in a derivative expression"
for i in range(underscore_position+1,len(varstr)):
if (varstr[i] == "D"):
deriv_order += 1
# Step 2a.4: Based on derivative order and rank,
# store the base gridfunction name in
# deriv__base_gridfunction_name[]
deriv__base_gridfunction_name.append(varstr[0:underscore_position]+
varstr[len(varstr)-deriv_order-rank:len(varstr)-deriv_order])
deriv__operator.append(varstr[underscore_position+1:len(varstr)-deriv_order-rank]+
varstr[len(varstr)-deriv_order:len(varstr)])
# Step 2b:
# Then check each base gridfunction to determine whether
# it is indeed registered as a gridfunction.
# If not, exit with error.
for basegf in deriv__base_gridfunction_name:
is_gf = False
for gf in gri.glb_gridfcs_list:
if basegf == str(gf.name):
is_gf = True
pass
if not is_gf:
print("Error: Attempting to take the derivative of "+basegf+", which is not a registered gridfunction.")
print(" Make sure your gridfunction name does not have any underscores in it!")
sys.exit(1)
# Step 2c:
# Check each derivative operator to make sure it is
# supported. If not, error out.
for i in range(len(deriv__operator)):
found_derivID = False
for derivID in ["dD","dupD","ddnD","dKOD"]:
if derivID in deriv__operator[i]:
found_derivID = True
if not found_derivID:
print("Error: Valid derivative operator in "+deriv__operator[i]+" not found.")
sys.exit(1)
# Step 2d (Upwinded derivatives algorithm, part 1):
# If an upwinding control vector is specified, determine
# which of the elements of the vector will be required.
# This ensures that those elements are read from memory.
# For example, if a symmetry axis is specified,
# upwind derivatives with respect to only
# two of the three dimensions are used. Here
# we find all directions used for upwinding.
if upwindcontrolvec != "":
upwind_directions_unsorted_withdups = []
for deriv_op in deriv__operator:
if "dupD" in deriv_op:
if deriv_op[len(deriv_op)-1].isdigit():
dirn = int(deriv_op[len(deriv_op)-1])
upwind_directions_unsorted_withdups.append(dirn)
else:
print("Error: Derivative operator "+deriv_op+" does not contain a direction")
sys.exit(1)
upwind_directions = []
if len(upwind_directions_unsorted_withdups)>0:
upwind_directions = superfast_uniq(upwind_directions_unsorted_withdups)
upwind_directions = sorted(upwind_directions,key=sp.default_sort_key)
# Step 3:
# Evaluate the finite difference stencil for each
# derivative operator,
# TODO: being careful not to needlessly recompute.
# Note: Each finite difference stencil consists
# of two parts:
# 1) The coefficient, and
# 2) The index corresponding to the coefficient.
# The former is stored as a rational number, and
# the latter as a simple string, such that e.g.,
# in 3D, the empty string corresponds to (i,j,k),
# the string "ip1" corresponds to (i+1,j,k),
# the string "ip1kp1" corresponds to (i+1,j,k+1),
# etc.
fdcoeffs = [[] for i in range(len(deriv__operator))]
fdstencl = [[[] for i in range(4)] for j in range(len(deriv__operator))]
for i in range(len(deriv__operator)):
fdcoeffs[i], fdstencl[i] = compute_fdcoeffs_fdstencl(deriv__operator[i])
# Step 4:
# Create C code to read gridfunctions from memory
# Step 4a: Compile list of points to read from memory
# for each gridfunction i, based on list
# provided in fdstencil[i][].
list_of_points_read_from_memory_with_duplicates = [[] for i in range(len(gri.glb_gridfcs_list))]
for j in range(len(deriv__base_gridfunction_name)):
derivgfname = deriv__base_gridfunction_name[j]
# Next find the corresponding gridfunction index:
for i in range(len(gri.glb_gridfcs_list)):
gfname = gri.glb_gridfcs_list[i].name
# If the gridfunction for the derivative matches, then
# add to the list of points read from memory:
if derivgfname == gfname:
for k in range(len(fdstencl[j])):
list_of_points_read_from_memory_with_duplicates[i].append(str(fdstencl[j][k][0]) + "," + \
str(fdstencl[j][k][1]) + "," + \
str(fdstencl[j][k][2]) + "," + \
str(fdstencl[j][k][3]))
# Step 4b: "Zeroth derivative" case:
# If gridfunction appears in expression not
# as derivative (i.e., by itself), it must
# be read from memory as well.
for expr in range(len(sympyexpr_list)):
for var in sympyexpr_list[expr].rhs.free_symbols:
vartype = gri.variable_type(var)
if vartype == "gridfunction":
for i in range(len(gri.glb_gridfcs_list)):
gfname = gri.glb_gridfcs_list[i].name
if gfname == str(var):
list_of_points_read_from_memory_with_duplicates[i].append("0,0,0,0")
# Step 4c: Remove duplicates when reading from memory;
# do not needlessly read the same variable
# from memory twice.
list_of_points_read_from_memory = [[] for i in range(len(gri.glb_gridfcs_list))]
for i in range(len(gri.glb_gridfcs_list)):
list_of_points_read_from_memory[i] = superfast_uniq(list_of_points_read_from_memory_with_duplicates[i])
# Step 4d: Minimize cache misses:
# Sort the list of points read from
# main memory by how they are stored
# in memory.
# Step 4d.i: Define a function that maps a gridpoint
# index (i,j,k,l) to a unique memory "address",
# which will correspond to the correct ordering
# of actual memory addresses.
#
# Input: a list of 4 indices, e.g., (i,j,k,l)
# corresponding to a gridpoint's *spatial*
# index in memory (thus we support up to
# 4D in space). If spatial dimension is
# less than 4D, then just set latter
# index/indices to zero. E.g., for 2D
# spatial indexing, set (i,j,0,0).
# Output: a single number, which when sorted
# will yield a unique "address" in memory
# such that consecutive addresses are
# consecutive in memory.
def unique_idx(idx4):
# os and sz are set *just for the purposes of ensuring indices are ordered in memory*
# Do not modify the values of os and sz.
os = 50 # offset
sz = 100 # assumed size in each direction
if par.parval_from_str("MemAllocStyle") == "210":
return str(int(idx4[0])+os + sz*( (int(idx4[1])+os) + sz*( (int(idx4[2])+os) + sz*( int(idx4[3])+os ) ) ))
elif par.parval_from_str("MemAllocStyle") == "012":
return str(int(idx4[3])+os + sz*( (int(idx4[2])+os) + sz*( (int(idx4[1])+os) + sz*( int(idx4[0])+os ) ) ))
else:
print("Error: MemAllocStyle = "+par.parval_from_str("MemAllocStyle")+" unsupported.")
sys.exit(1)
# Step 4d.ii: For each gridfunction and
# point read from memory, call unique_idx,
# then sort according to memory "address"
# Input: list_of_points_read_from_memory[gridfunction][point],
# gri.glb_gridfcs_list[gridfunction]
# Output: 1) A list of points to be read from
# memory, sorted according to memory
# "address":
# sorted_list_of_points_read_from_memory[gridfunction][point]
# 2) A list containing the gridfunction
# read at each point, with the number
# of elements corresponding exactly
# to the total number of points read
# from memory for all gridfunctions:
# read_from_memory_gf[]
read_from_memory_gf = []
sorted_list_of_points_read_from_memory = [[] for i in range(len(gri.glb_gridfcs_list))]
for gfidx in range(len(gri.glb_gridfcs_list)):
# Continue only if reading at least one point of gfidx from memory.
# The sorting algorithm at the end of this code block is not
# well-defined (will throw an error) if no points of gfidx are
# read from memory.
if len(list_of_points_read_from_memory[gfidx]) > 0:
read_from_memory_index = []
for idx in list_of_points_read_from_memory[gfidx]:
read_from_memory_gf.append(gri.glb_gridfcs_list[gfidx])
idxsplit = idx.split(',')
idx4 = [int(idxsplit[0]),int(idxsplit[1]),int(idxsplit[2]),int(idxsplit[3])]
read_from_memory_index.append(unique_idx(idx4))
# https://stackoverflow.com/questions/13668393/python-sorting-two-lists
UNUSEDlist, sorted_list_of_points_read_from_memory[gfidx] = \
[list(x) for x in zip(*sorted(zip(read_from_memory_index, list_of_points_read_from_memory[gfidx]),
key=itemgetter(0)))]
# Step 4e: Create the full C code string
# for reading from memory:
# if DIM==4:
# input: [i,j,k,l]
# output: "i0+i,i1+j,i2+k,i3+l"
# if DIM==3:
# input: [i,j,k,l]
# output: "i0+i,i1+j,i2+k"
# etc.
def ijkl_string(idx4):
DIM = par.parval_from_str("DIM")
retstring = ""
for i in range(DIM):
if i>0:
# Add a comma
retstring += ","
retstring += "i"+str(i)+"+"+str(idx4[i])
return retstring.replace("+-", "-").replace("+0", "")
def out__type_var(in_var,AddPrefix_for_UpDownWindVars=True):
varname = str(in_var)
# Disable prefixing upwinded and downwinded variables
# if the upwind control vector algorithm is disabled.
if upwindcontrolvec == "":
AddPrefix_for_UpDownWindVars = False
if AddPrefix_for_UpDownWindVars:
if "_dupD" in varname: # Variables suffixed with "_dupD" are set
# to be the "pure" upwinded derivative,
# before the upwinding algorithm has been
# applied. However, when they are used
# in the RHS expressions, it is assumed
# that the up. algorithm has been applied.
# To ensure consistency we rename all
# _dupD suffixed variables as
# _dupDPUREUPWIND, and use them as input
# into the upwinding algorithm. The output
# will be the original _dupD variable.
varname = "UpwindAlgInput"+varname
if "_ddnD" in varname: # For consistency with _dupD
varname = "UpwindAlgInput"+varname
if outCparams.SIMD_enable == "True":
return "const REAL_SIMD_ARRAY " + varname
else:
TYPE = par.parval_from_str("PRECISION")
return "const "+ TYPE + " " + varname
def varsuffix(idx4):
if idx4 == [0,0,0,0]:
return ""
return "_"+ijkl_string(idx4).replace(",","_").replace("+","p").replace("-","m")
def read_from_memory_Ccode_onept(gfname,idx):
idxsplit = idx.split(',')
idx4 = [int(idxsplit[0]),int(idxsplit[1]),int(idxsplit[2]),int(idxsplit[3])]
gf_array_name = "in_gfs" # Default array name.
gfaccess_str = gri.gfaccess(gf_array_name,gfname,ijkl_string(idx4))
if outCparams.SIMD_enable == "True":
retstring = out__type_var(gfname) + varsuffix(idx4) +" = ReadSIMD(&" + gfaccess_str + ");"
else:
retstring = out__type_var(gfname) + varsuffix(idx4) +" = " + gfaccess_str + ";"
return retstring+"\n"
read_from_memory_Ccode = ""
count = 0
for gfidx in range(len(gri.glb_gridfcs_list)):
for pt in range(len(sorted_list_of_points_read_from_memory[gfidx])):
read_from_memory_Ccode += read_from_memory_Ccode_onept(read_from_memory_gf[count].name,
sorted_list_of_points_read_from_memory[gfidx][pt])
count += 1
# Step 5: Output C code. C code consists of three parts
# a) Read gridfunctions from memory at needed pts.
# b) Perform arithmetic needed for input expressions
# provided in sympyexpr_list[].rhs and associated
# finite differences.
# c) Write output to gridfunctions specified in
# sympyexpr_list[].lhs.
def indent_Ccode(Ccode):
Ccodesplit = Ccode.splitlines()
outstring = ""
for i in range(len(Ccodesplit)):
outstring += outCparams.preindent+indent+Ccodesplit[i]+'\n'
return outstring
# Step 5a: Read gridfunctions from memory at needed pts.
# *** No need to do anything here; already set in
# string "read_from_memory_Ccode". ***
# FIXME: Update these code comments:
# Step 5b: Perform arithmetic needed for finite differences
# associated with input expressions provided in
# sympyexpr_list[].rhs.
# Note that exprs and lhsvarnames contain
# i) finite difference expressions (constructed
# in steps above) and associated variable names,
# and
# ii) Input expressions sympyexpr_list[], which
# in general depend on finite difference
# variables.
exprs = []
lhsvarnames = []
# Step 5b.i: Output finite difference expressions to
# Coutput string
for i in range(len(list_of_deriv_vars)):
exprs.append(sp.sympify(0)) # Append a new element to the list of derivative expressions.
lhsvarnames.append(out__type_var(list_of_deriv_vars[i]))
var = deriv__base_gridfunction_name[i]
for j in range(len(fdcoeffs[i])):
varname = str(var)+varsuffix(fdstencl[i][j])
exprs[i] += fdcoeffs[i][j]*sp.sympify(varname)
# Multiply each expression by the appropriate power
# of 1/dx[i]
invdx = []
for d in range(par.parval_from_str("DIM")):
invdx.append(sp.sympify("invdx"+str(d)))
# First-order or Kreiss-Oliger derivatives:
if (len(deriv__operator[i]) == 5 and "dKOD" in deriv__operator[i]) or \
(len(deriv__operator[i]) == 3 and "dD" in deriv__operator[i]) or \
(len(deriv__operator[i]) == 5 and ("dupD" in deriv__operator[i] or "ddnD" in deriv__operator[i])):
dirn = int(deriv__operator[i][len(deriv__operator[i])-1])
exprs[i] *= invdx[dirn]
# Second-order derivs:
elif len(deriv__operator[i]) == 5 and "dDD" in deriv__operator[i]:
dirn1 = int(deriv__operator[i][len(deriv__operator[i]) - 2])
dirn2 = int(deriv__operator[i][len(deriv__operator[i]) - 1])
exprs[i] *= invdx[dirn1]*invdx[dirn2]
else:
print("Error: was unable to parse derivative operator: ",deriv__operator[i])
sys.exit(1)
# Step 5b.ii: If upwind control vector is specified,
# add upwind control vectors to the
# derivative expression list, so its
# needed elements are read from memory.
if upwindcontrolvec != "":
for i in range(len(upwind_directions)):
exprs.append(upwindcontrolvec[upwind_directions[i]])
lhsvarnames.append(out__type_var("UpwindControlVectorU"+str(upwind_directions[i])))
# Step 5b.iii: Output useful code comment regarding
# which step we are on. *At most* this
# is a 3-step process:
# 1. Read from memory & compute FD stencils,
# 2. Perform upwinding, and
# 3. Evaluate remaining expressions+write
# results to main memory.
NRPy_FD_StepNumber = 1
NRPy_FD__Number_of_Steps = 1
if len(list_of_deriv_vars) > 0:
NRPy_FD__Number_of_Steps += 1
if upwindcontrolvec != "" and len(upwind_directions) > 0:
NRPy_FD__Number_of_Steps += 1
if len(read_from_memory_Ccode) > 0:
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps)+
": Read from main memory and compute finite difference stencils:\n */\n")
NRPy_FD_StepNumber = NRPy_FD_StepNumber + 1
default_CSE_varprefix = outCparams.CSE_varprefix
# Prefix chosen CSE variables with "FD", for the finite difference coefficients:
Coutput += indent_Ccode(outputC(exprs,lhsvarnames,"returnstring",params=params + ",CSE_varprefix="+default_CSE_varprefix+"FD,includebraces=False,SIMD_const_suffix=_FDcoeff",
prestring=read_from_memory_Ccode))
# Step 5b.iv: Implement control-vector upwinding algorithm.
if upwindcontrolvec != "":
if len(upwind_directions) > 0:
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps) +
": Implement upwinding algorithm:\n */\n")
NRPy_FD_StepNumber = NRPy_FD_StepNumber + 1
if outCparams.SIMD_enable == "True":
Coutput += """
const double tmp_upwind_Integer_1 = 1.000000000000000000000000000000000;
const REAL_SIMD_ARRAY upwind_Integer_1 = ConstSIMD(tmp_upwind_Integer_1);
const double tmp_upwind_Integer_0 = 0.000000000000000000000000000000000;
const REAL_SIMD_ARRAY upwind_Integer_0 = ConstSIMD(tmp_upwind_Integer_0);
"""
for dirn in upwind_directions:
Coutput += indent_Ccode(out__type_var("UpWind" + str(dirn)) + " = UPWIND_ALG(UpwindControlVectorU" + str(dirn) + ");\n")
upwindU = [sp.sympify(0) for i in range(par.parval_from_str("DIM"))]
for dirn in upwind_directions:
upwindU[dirn] = sp.sympify("UpWind"+str(dirn))
for i in range(len(list_of_deriv_vars)):
if len(deriv__operator[i]) == 5 and ("dupD" in deriv__operator[i]):
var_dupD = sp.sympify("UpwindAlgInput"+str(list_of_deriv_vars[i]))
var_ddnD = sp.sympify("UpwindAlgInput"+str(list_of_deriv_vars[i]).replace("_dupD","_ddnD"))
upwind_dirn = int(deriv__operator[i][len(deriv__operator[i])-1])
upwind_expr = upwindU[upwind_dirn]*(var_dupD - var_ddnD) + var_ddnD
# For convenience, we require out__type_var() above to
# prefix up/downwinded variables with "UpwindAlgInput".
# Here we do not wish to have this prefix.
Coutput += indent_Ccode(outputC(upwind_expr,
out__type_var(str(list_of_deriv_vars[i]),AddPrefix_for_UpDownWindVars=False),
"returnstring",params=params + ",includebraces=False"))
# Step 5b.v: Add input RHS & LHS expressions from
# sympyexpr_list[]
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps) +
": Evaluate SymPy expressions and write to main memory:\n */\n")
exprs = []
lhsvarnames = []
for i in range(len(sympyexpr_list)):
exprs.append(sympyexpr_list[i].rhs)
if outCparams.SIMD_enable == "True":
lhsvarnames.append("const REAL_SIMD_ARRAY __RHS_exp_"+str(i))
else:
lhsvarnames.append(sympyexpr_list[i].lhs)
# Step 5c: Write output to gridfunctions specified in
# sympyexpr_list[].lhs.
write_to_mem_string = ""
if outCparams.SIMD_enable == "True":
for i in range(len(sympyexpr_list)):
write_to_mem_string += "WriteSIMD(&"+sympyexpr_list[i].lhs+", __RHS_exp_"+str(i)+");\n"
Coutput += indent_Ccode(outputC(exprs,lhsvarnames,"returnstring", params = params+",includebraces=False,preindent=0", prestring="",poststring=write_to_mem_string))
# Step 6: Add consistent indentation to the output end brace.
# See Step 0.b for corresponding start brace.
if outCparams.includebraces == "True":
Coutput += outCparams.preindent+"}\n"
# Step 7: Output the C code in desired format: stdout, string, or file.
if filename == "stdout":
print(Coutput)
elif filename == "returnstring":
return Coutput+'\n'
else:
# Output to the file specified by outCfilename
with open(filename, outCparams.outCfileaccess) as file:
file.write(Coutput)
successstr = ""
if outCparams.outCfileaccess == "a":
successstr = "Appended "
elif outCparams.outCfileaccess == "w":
successstr = "Wrote "
print(successstr + "to file \"" + filename + "\"")
# print(gri.glb_gridfcs_list[1].name,list_of_points_read_from_memory[1])
# Define the to-be-inverted matrix, A.
# We define A row-by-row, according to the prescription
# derived in notes/notes.pdf, via the following pattern
# that applies for arbitrary order.
#
# As an example, consider a 5-point finite difference
# stencil (4th-order accurate), where we wish to compute
# some derivative at the center point.
#
# Then A is given by:
#
# -2^0 -1^0 1 1^0 2^0
# -2^1 -1^1 0 1^1 2^1
# -2^2 -1^2 0 1^2 2^2
# -2^3 -1^3 0 1^3 2^3
# -2^4 -1^4 0 1^4 2^4
#
# Then right-multiplying A^{-1}
# by (1 0 0 0 0)^T will yield 0th deriv. stencil
# by (0 1 0 0 0)^T will yield 1st deriv. stencil
# by (0 0 1 0 0)^T will yield 2nd deriv. stencil
# etc.
#
# Next suppose we want an upwinded, 4th-order accurate
# stencil. For this case, A is given by:
#
# -1^0 1 1^0 2^0 3^0
# -1^1 0 1^1 2^1 3^1
# -1^2 0 1^2 2^2 3^2
# -1^3 0 1^3 2^3 3^3
# -1^4 0 1^4 2^4 3^4
#
# ... and similarly for the downwinded derivative.
#
# Finally, let's consider a 3rd-order accurate
# stencil. This would correspond to an in-place
# upwind stencil with stencil radius of 2 gridpoints,
# where other, centered derivatives are 4th-order
# accurate. For this case, A is given by:
#
# -1^0 1 1^0 2^0
# -1^1 0 1^1 2^1
# -1^2 0 1^2 2^2
# -1^3 0 1^3 2^3
# -1^4 0 1^4 2^4
#
# ... and similarly for the downwinded derivative.
#
# The general pattern is as follows:
#
# 1) The top row is all 1's,
# 2) If the second row has N elements (N must be odd),
# .... then the radius of the stencil is rs = (N-1)/2
# .... and the j'th row e_j = j-rs-1. For example,
# .... for 4th order, we have rs = 2
# .... j | element
# .... 1 | -2
# .... 2 | -1
# .... 3 | 0
# .... 4 | 1
# .... 5 | 2
# 3) The L'th row, L>2 will be the same as the second
# .... row, but with each element e_j -> e_j^(L-1)
# A1 is used later to validate the inverted
# matrix.
def compute_fdcoeffs_fdstencl(derivstring,FDORDER=-1):
# Step 0: Set finite differencing order, stencil size, and up/downwinding
if FDORDER == -1:
FDORDER = par.parval_from_str("FD_CENTDERIVS_ORDER")
if "dKOD" in derivstring:
FDORDER += par.parval_from_str("FD_KO_ORDER__CENTDERIVS_PLUS")
STENCILSIZE = FDORDER+1
UPDOWNWIND = 0
if "dupD" in derivstring:
UPDOWNWIND = 1
elif "ddnD" in derivstring:
UPDOWNWIND = -1
# Step 1: Set up matrix based on the stencil size (FDORDER+1).
# See documentation above for details on how this
# matrix is set up.
M = sp.zeros(STENCILSIZE,STENCILSIZE)
for i in range(STENCILSIZE):
for j in range(STENCILSIZE):
if i == 0:
M[(i,j)] = 1 # Setting n^0 = 1 for all n, including n=0, because this matches the pattern
else:
dist_from_xeq0_col = j - sp.Rational((STENCILSIZE - 1),2) + UPDOWNWIND
if dist_from_xeq0_col==0:
M[(i,j)] = 0
else:
M[(i, j)] = dist_from_xeq0_col**(i)
Minv = sp.zeros(STENCILSIZE,STENCILSIZE)
Minv = M**(-1)
# Step 2:
# Based on the input derivative string,
# pick out the relevant row of the matrix
# inverse, as outlined in the detailed code
# comments prior to this function definition.
derivtype = "FirstDeriv"
matrixrow = 1
if "DDD" in derivstring:
print("Error: Only derivatives up to second order currently supported.")
print(" Feel free to contribute to NRPy+ to extend its functionality!")
sys.exit(1)
elif "DD" in derivstring:
if derivstring[len(derivstring)-1] == derivstring[len(derivstring)-2]:
# Assuming i==j, we call \partial_i \partial_j gf an "unmixed" second derivative,
# or more simply, just "SecondDeriv":
derivtype = "SecondDeriv"
matrixrow = 2
else:
# Assuming i!=j, we call \partial_i \partial_j gf a MIXED second derivative,
# which is computed using a composite of first derivative operations.
derivtype = "MixedSecondDeriv"
elif "dKOD" in derivstring:
derivtype = "KreissOligerDeriv"
matrixrow = STENCILSIZE - 1
else:
# Up/downwinded and first derivs are all of "FirstDeriv" type
pass
# Step 3:
# Set finite difference coefficients
# and stencil points corresponding to
# each finite difference coefficient.
fdcoeffs = []
fdstencl = []
if derivtype != "MixedSecondDeriv":
for i in range(STENCILSIZE):
idx4 = [0, 0, 0, 0]
# First compute finite difference coefficient.
fdcoeff = sp.factorial(matrixrow)*Minv[(i,matrixrow)]
# Do not store fdcoeff or fdstencil if
# finite difference coefficient is zero.
if fdcoeff != 0:
fdcoeffs.append(fdcoeff)
if derivtype == "KreissOligerDeriv":
fdcoeffs[i] *= (-1)**(sp.Rational((STENCILSIZE+1),2))/2**matrixrow
# Next store finite difference stencil point
# corresponding to coefficient.
gridpt_posn = i - int((STENCILSIZE-1)/2) + UPDOWNWIND
if gridpt_posn != 0:
dirn = int(derivstring[len(derivstring)-1])
idx4[dirn] = gridpt_posn
fdstencl.append(idx4)
else:
# Mixed second derivative finite difference coeffs
# consist of products of first deriv coeffs,
# defined in first Minv matrix row.
for i in range(STENCILSIZE):
for j in range(STENCILSIZE):
idx4 = [0, 0, 0, 0]
# First compute finite difference coefficient.
fdcoeff = (sp.factorial(matrixrow)*Minv[(i,matrixrow)]) * \
(sp.factorial(matrixrow)*Minv[(j,matrixrow)])
# Do not store fdcoeff or fdstencil if
# finite difference coefficient is zero.
if fdcoeff != 0:
fdcoeffs.append(fdcoeff)
# Next store finite difference stencil point
# corresponding to coefficient.
gridpt_posn1 = i - int((STENCILSIZE - 1) / 2)
gridpt_posn2 = j - int((STENCILSIZE - 1) / 2)
dirn1 = int(derivstring[len(derivstring) - 1])
dirn2 = int(derivstring[len(derivstring) - 2])
idx4[dirn1] = gridpt_posn1
idx4[dirn2] = gridpt_posn2
fdstencl.append(idx4)
return fdcoeffs,fdstencl
# grid.py: functions & parameters related to numerical grids
# functions: Automatic loop output, output C code needed for gridfunction memory I/O, gridfunction registration
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import NRPy_param_funcs as par # NRPy+: Parameter interface
import sympy as sp # Import SymPy, a computer algebra system written entirely in Python
from collections import namedtuple # Standard Python `collections` module: defines named tuples data structure
# Initialize globals related to the grid
glb_gridfcs_list = []
glb_gridfc = namedtuple('gridfunction', 'gftype name rank DIM')
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "GridFuncMemAccess", "SENRlike"))
par.initialize_param(par.glb_param("char", thismodule, "MemAllocStyle","210"))
par.initialize_param(par.glb_param("int", thismodule, "DIM", 3))
Nxx = par.Cparameters("int", thismodule,["Nxx0","Nxx1","Nxx2"],[64,32,64]) # Default to 64x32x64 grid
Nxx_plus_2NGHOSTS = par.Cparameters("int", thismodule,
["Nxx_plus_2NGHOSTS0","Nxx_plus_2NGHOSTS1","Nxx_plus_2NGHOSTS2"],
[ 70, 38, 70]) # Default to 64x32x64 grid w/ NGHOSTS=3
xx = par.Cparameters("REAL",thismodule,[ "xx0", "xx1", "xx2"],1e300) # These are C variables, not parameters, and
# will be overwritten; best to initialize to crazy
# number to ensure they are overwritten!
dxx = par.Cparameters("REAL",thismodule,[ "dxx0", "dxx1", "dxx2"],0.1)
invdx = par.Cparameters("REAL",thismodule,[ "invdx0", "invdx1", "invdx2"],1.0)
def variable_type(var):
var_is_gf = False
for gf in range(len(glb_gridfcs_list)):
if str(var) == glb_gridfcs_list[gf].name:
var_is_gf = True
var_is_parameter = False
# for paramname in range(len(par.glb_params_list)):
# if str(var) == par.glb_params_list[paramname].parname:
# var_is_parameter = True
for paramname in range(len(par.glb_Cparams_list)):
if str(var) == par.glb_Cparams_list[paramname].parname:
var_is_parameter = True
if var_is_parameter and var_is_gf:
print("Error: variable "+str(var)+" is registered both as a gridfunction and as a Cparameter.")
sys.exit(1)
if not (var_is_parameter or var_is_gf):
return "other"
if var_is_parameter:
return "Cparameter"
if var_is_gf:
return "gridfunction"
def find_gftype(varname):
for gf in glb_gridfcs_list:
if gf.name == varname:
return gf.gftype
def gfaccess(gfarrayname = "", varname = "", ijklstring = ""):
found_registered_gf = False
for gf in glb_gridfcs_list:
if gf.name == varname:
if found_registered_gf:
print("Error: found duplicate gridfunction name: "+gf.name)
sys.exit(1)
found_registered_gf = True
if not found_registered_gf:
print("Error: gridfunction \""+varname+"\" is not registered!")
sys.exit(1)
gftype = find_gftype(varname)
DIM = par.parval_from_str("DIM")
retstring = ""
if par.parval_from_str("GridFuncMemAccess") == "SENRlike":
if gfarrayname == "":
print("Error: GridFuncMemAccess = SENRlike requires gfarrayname be passed to gfaccess()")
sys.exit(1)
# FIXME: if gftype == "AUX" then override gfarrayname to aux_gfs[].
# This enables expressions containing a mixture of AUX and EVOL
# gridfunctions, though in a slightly hacky way.
if gftype == "AUX":
gfarrayname = "aux_gfs"
elif gftype == "AUXEVOL":
gfarrayname = "auxevol_gfs"
# Return gfarrayname[IDX3(varname,i0)] for DIM=1, gfarrayname[IDX3(varname,i0,i1)] for DIM=2, etc.
retstring += gfarrayname + "[IDX" + str(DIM+1) + "(" + varname.upper()+"GF" + ", "
elif par.parval_from_str("GridFuncMemAccess") == "ETK":
# Return varname[CCTK_GFINDEX3D(i0,i1,i2)] for DIM=3. Error otherwise
if DIM != 3:
print("Error: GridFuncMemAccess = ETK currently requires that gridfunctions be 3D. Can be easily extended.")
sys.exit(1)
if gfarrayname == "rhs_gfs":
retstring += varname + "_rhsGF" + "[CCTK_GFINDEX"+str(DIM)+"D(cctkGH, "
else:
retstring += varname + "GF" + "[CCTK_GFINDEX"+str(DIM)+"D(cctkGH, "
else:
print("grid::GridFuncMemAccess = "+par.parval_from_str("GridFuncMemAccess")+" not supported")
sys.exit(1)
if ijklstring == "":
for i in range(DIM):
retstring += "i"+str(i)
if i != DIM-1:
retstring += ', '
else:
retstring += ijklstring
return retstring + ")]"
# Gridfunction basenames cannot end in integers.
# For example, rank-1 gridfunction vecU1 has
# basename "vecU". Thus this gridfunction has
# a valid basename. If we instead defined a
# scalar gridfunction "u2", this would have a
# basename of "u2" -- not a valid basename.
# Being so strict enables us to determine
# quickly what a gridfunction is by its name
# alone, which is useful, e.g., when setting
# up parity boundary conditions.
def verify_gridfunction_basename_is_valid(gf_basename):
# First check for zero-length basenames:
if len(gf_basename) == 0:
print("Error: tried to register gridfunction without a name!")
sys.exit(1)
# https://stackoverflow.com/questions/1303243/how-to-find-out-if-a-python-object-is-a-string
if sys.version_info[0] < 3:
if not isinstance(gf_basename,basestring):
print("ERROR: gf_names must be strings")
sys.exit(1)
else:
if not isinstance(gf_basename, str):
print("ERROR: gf_names must be strings")
sys.exit(1)
if len(gf_basename) > 0 and gf_basename[-1].isdigit():
print("Error: tried to register gridfunction with base name: "+gf_basename)
print(" Gridfunctions with base names ending in an integer is forbidden; pick a new name.")
sys.exit(1)
import sys
def register_gridfunctions(gf_type,gf_names,rank=0,is_indexed=False,DIM=3):
# Step 0: Sanity check
if (rank > 0 and is_indexed == False) or (rank == 0 and is_indexed == True):
print("Error: Attempted to register *indexed* gridfunction(s) with rank 0, or *scalar* gridfunctions with rank>0.")
print(" Gridfunctions = ",gf_names)
sys.exit(1)
# Step 1: convert gf_names to a list if it's not already a list
if type(gf_names) is not list:
gf_namestmp = [gf_names]
gf_names = gf_namestmp
# Step 2: if the gridfunction is not indexed, then
# gf_names == base names. Check that the
# gridfunction basenames are valid:
if is_indexed==False:
for i in range(len(gf_names)):
verify_gridfunction_basename_is_valid(gf_names[i])
# Step 3: Verify that gridfunction type is valid.
if not (gf_type == "EVOL" or gf_type == "AUX" or gf_type == "AUXEVOL"):
print("Error in registering gridfunction(s) with unsupported type "+gf_type+".")
print("Supported types include \"EVOL\" for gridfunctions related to evolved quantities or \"AUX\" for all others.")
sys.exit(1)
# Step 4: Check for duplicate grid function registrations. If:
# a) A duplicate is found, error out. Otherwise
# b) Append to list of gridfunctions, stored in glb_gridfcs_list[].
for i in range(len(gf_names)):
for j in range(len(glb_gridfcs_list)):
if gf_names[i] == glb_gridfcs_list[j].name:
print("Error: Tried to register the gridfunction \""+gf_names[i]+"\" twice (ignored type)\n\n")
sys.exit(1)
# If no duplicate found, append to "gridfunctions" list:
glb_gridfcs_list.append(glb_gridfc(gf_type,gf_names[i],rank,DIM))
# Step 5: Return SymPy object corresponding to symbol or
# list of symbols representing gridfunction in
# SymPy expression
OBJ_TMPS = []
for i in range(len(gf_names)):
OBJ_TMPS.append(sp.symbols(gf_names[i], real=True))
# OBJ_TMPS.append(sp.sympify(gf_names[i]))
if len(gf_names)==1:
return OBJ_TMPS[0]
return OBJ_TMPS
# Given output directory "outdir" as input, the
# following function outputs a file called
# "outdir/gridfunction_defines.h", which
# #define's all the gridfunction aliases, and
# returns two lists, corresponding to the
# names (strings) of the evolved and auxiliary
# gridfunction names respectively.
#
# For example, if we define only two gridfunctions uu and vv,
# which are evolved quantities (i.e., represent
# the solution of the PDEs we are solving and are
# registered with gftype == "EVOL"), then
# this function will create a file with the following
# content:
#
# | /* This file is automatically generated by NRPy+. Do not edit. */
# | /* EVOLVED VARIABLES: */
# | #define NUM_EVOL_GFS 2
# | #define UUGF 0
# | #define VVGF 1
# |
# | /* AUXILIARY VARIABLES: */
# | #define NUM_AUX_GFS 0
#
# The function will return two lists: the lists of
# EVOL and AUX gridfunction names, respectively.
# For this example, the first list (all gridfunctions
# registered as EVOL) will be ["uu","vv"], and the
# second (all gridfunctions registered as AUX) will
# be the empty list: []
def gridfunction_lists():
evolved_variables_list = []
auxiliary_variables_list = []
auxevol_variables_list = []
for i in range(len(glb_gridfcs_list)):
if glb_gridfcs_list[i].gftype == "EVOL":
evolved_variables_list.append(glb_gridfcs_list[i].name)
if glb_gridfcs_list[i].gftype == "AUX":
auxiliary_variables_list.append(glb_gridfcs_list[i].name)
if glb_gridfcs_list[i].gftype == "AUXEVOL":
auxevol_variables_list.append(glb_gridfcs_list[i].name)
# Next we alphabetize the lists
evolved_variables_list.sort()
auxiliary_variables_list.sort()
auxevol_variables_list.sort()
return evolved_variables_list,auxiliary_variables_list,auxevol_variables_list
def output__gridfunction_defines_h__return_gf_lists(outdir):
evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gridfunction_lists()
# Finally we set up the #define statements:
with open(outdir+"/gridfunction_defines.h", "w") as file:
file.write("/* This file is automatically generated by NRPy+. Do not edit. */\n\n")
file.write("/* EVOLVED VARIABLES: */\n")
file.write("#define NUM_EVOL_GFS "+str(len(evolved_variables_list))+"\n")
for i in range(len(evolved_variables_list)):
file.write("#define "+evolved_variables_list[i].upper()+"GF\t"+str(i)+"\n")
file.write("\n\n /* AUXILIARY VARIABLES: */\n")
file.write("#define NUM_AUX_GFS " + str(len(auxiliary_variables_list)) + "\n")
for i in range(len(auxiliary_variables_list)):
file.write("#define " + auxiliary_variables_list[i].upper() + "GF\t" + str(i) + "\n")
file.write("\n\n /* AUXEVOL VARIABLES: */\n")
file.write("#define NUM_AUXEVOL_GFS "+str(len(auxevol_variables_list))+"\n")
for i in range(len(auxevol_variables_list)):
file.write("#define "+auxevol_variables_list[i].upper()+"GF\t"+str(i)+"\n")
return evolved_variables_list,auxiliary_variables_list,auxevol_variables_list
# indexedexp.py: functions related to indexed expressions,
# including e.g., tensors and pseudotensors:
# Step 1: Load needed modules
import NRPy_param_funcs as par
import grid as gri
import sympy as sp
import sys
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "symmetry_axes", ""))
def zerorank1(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [sp.sympify(0) for i in range(DIM)]
def zerorank2(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[sp.sympify(0) for i in range(DIM)] for j in range(DIM)]
def zerorank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[[sp.sympify(0) for i in range(DIM)] for j in range(DIM)] for k in range(DIM)]
def zerorank4(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[[[sp.sympify(0) for i in range(DIM)] for j in range(DIM)] for k in range(DIM)] for l in range(DIM)]
def apply_symmetry_condition_to_derivatives(IDX_OBJ):
symmetry_axes = par.parval_from_str("indexedexp::symmetry_axes")
if symmetry_axes == "":
return IDX_OBJ
rank = 1
if isinstance(IDX_OBJ[0], list):
if not isinstance(IDX_OBJ[0][0], list):
rank = 2
elif not isinstance(IDX_OBJ[0][0][0], list):
rank = 3
elif not isinstance(IDX_OBJ[0][0][0][0], list):
rank = 4
else:
print("Error: could not figure out rank for ",IDX_OBJ)
sys.exit(1)
def does_IDXOBJ_perform_derivative_across_symmetry_axis(idxobj_str):
returnval = False
if "_d" in idxobj_str:
# First we find the order of the derivative:
deriv_order = 0
underscore_position = -1000
for i in range(len(idxobj_str)-1):
if idxobj_str[i] == "_" and idxobj_str[i+1]=="d":
# The order of the derivative is given by the number of D's in a row after the _d:
for k in range(i+2,len(idxobj_str)):
if idxobj_str[k] == "D":
deriv_order = deriv_order + 1
if deriv_order > 2:
print("Error. Derivative order > 2 not supported. Found derivative order = "+str(deriv_order))
sys.exit(1)
end_idx_of_idxobj_str = len(idxobj_str)-1
for j in range(end_idx_of_idxobj_str,end_idx_of_idxobj_str-deriv_order,-1):
if idxobj_str[j] in symmetry_axes:
return True
return False
if rank == 1:
DIM = len(IDX_OBJ)
for i0 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0])) == True:
IDX_OBJ[i0] = sp.sympify(0)
if rank == 2:
DIM = len(IDX_OBJ[0])
for i0 in range(DIM):
for i1 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1])) == True:
IDX_OBJ[i0][i1] = sp.sympify(0)
if rank == 3:
DIM = len(IDX_OBJ[0][0])
for i0 in range(DIM):
for i1 in range(DIM):
for i2 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1][i2])) == True:
IDX_OBJ[i0][i1][i2] = sp.sympify(0)
if rank == 4:
DIM = len(IDX_OBJ[0][0][0])
for i0 in range(DIM):
for i1 in range(DIM):
for i2 in range(DIM):
for i3 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1][i2][i3])) == True:
IDX_OBJ[i0][i1][i2][i3] = sp.sympify(0)
return IDX_OBJ
def declarerank1(objname, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [sp.sympify(objname + str(i)) for i in range(DIM)]
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def register_gridfunctions_for_single_rank1(gf_type,gf_basename, DIM=-1):
# Step 0: Verify the gridfunction basename is valid:
gri.verify_gridfunction_basename_is_valid(gf_basename)
# Step 1: Declare a list of SymPy variables,
# where IDX_OBJ_TMP[i] = gf_basename+str(i)
IDX_OBJ_TMP = declarerank1(gf_basename, DIM)
# Step 2: Register each gridfunction
if DIM==-1:
DIM = par.parval_from_str("DIM")
gf_list = []
for i in range(DIM):
gf_list.append(str(IDX_OBJ_TMP[i]))
gri.register_gridfunctions(gf_type, gf_list, rank=1, is_indexed=True, DIM=DIM)
# Step 3: Return array of SymPy variables
return IDX_OBJ_TMP
def declarerank2(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[sp.sympify(objname + str(i) + str(j)) for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
if symmetry_option == "sym01":
if (j < i):
# j<i in g_{ij} would indicate, e.g., g_{21}.
# By this convention, we must set
# g_{21} = g_{12}:
IDX_OBJ_TMP[i][j] = IDX_OBJ_TMP[j][i]
elif symmetry_option == "nosym":
pass
else:
print("Error: symmetry option " + symmetry_option + " unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def register_gridfunctions_for_single_rank2(gf_type,gf_basename, symmetry_option, DIM=-1):
# Step 0: Verify the gridfunction basename is valid:
gri.verify_gridfunction_basename_is_valid(gf_basename)
# Step 1: Declare a list of lists of SymPy variables,
# where IDX_OBJ_TMP[i][j] = gf_basename+str(i)+str(j)
IDX_OBJ_TMP = declarerank2(gf_basename,symmetry_option, DIM)
# Step 2: register each gridfunction, being careful not
# not to store duplicates due to rank-2 symmetries.
if DIM==-1:
DIM = par.parval_from_str("DIM")
# Register only unique gridfunctions. Otherwise
# rank-2 symmetries might result in duplicates
gf_list = []
for i in range(DIM):
for j in range(DIM):
save = True
for l in range(len(gf_list)):
if gf_list[l] == str(IDX_OBJ_TMP[i][j]):
save = False
if save == True:
gf_list.append(str(IDX_OBJ_TMP[i][j]))
gri.register_gridfunctions(gf_type,gf_list,rank=2, is_indexed=True, DIM=DIM)
# Step 3: Return array of SymPy variables
return IDX_OBJ_TMP
def declarerank3(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[[sp.sympify(objname + str(i) + str(j) + str(k)) for k in range(DIM)] for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
if symmetry_option == "sym01":
if j < i:
IDX_OBJ_TMP[i][j][k] = IDX_OBJ_TMP[j][i][k]
if symmetry_option == "sym12":
if k < j:
IDX_OBJ_TMP[i][j][k] = IDX_OBJ_TMP[i][k][j]
if not (symmetry_option == "sym01" or symmetry_option == "sym12" or symmetry_option == "nosym"):
print("Error: symmetry option " + symmetry_option + " unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def declarerank4(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[[[sp.sympify(objname + str(i) + str(j) + str(k) + str(l)) for l in range(DIM)] for k in range(DIM)] for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
IDX_OBJ_TMP[i][j][k][l] = sp.sympify(objname + str(i) + str(j) + str(k) + str(l))
if symmetry_option == "sym01" or symmetry_option == "sym01_sym23":
if(j < i):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[j][i][k][l]
if symmetry_option == "sym12":
if(k < j):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[i][k][j][l]
if symmetry_option == "sym23" or symmetry_option == "sym01_sym23":
if(l < k):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[i][j][l][k]
if not (symmetry_option=="sym01" or symmetry_option=="sym23" or symmetry_option=="sym01_sym23" or symmetry_option=="none"):
print("Error: symmetry option "+symmetry_option+" unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
# We use the following functions to evaluate 3-metric inverses
def symm_matrix_inverter3x3(a):
# It is far more efficient to write out the matrix determinant and inverse by hand
# instead of using symmetry_optionPy's built-in functions, since the matrix is symmetric.
outDET = -a[0][2]**2*a[1][1] + 2*a[0][1]*a[0][2]*a[1][2] - \
a[0][0]*a[1][2]**2 - a[0][1]**2*a[2][2] + \
a[0][0]*a[1][1]*a[2][2]
outINV = [[sp.sympify(0) for i in range(3)] for j in range(3)]
# First fill in the upper-triangle of the gPhysINV matrix...
outINV[0][0] = (-a[1][2]**2 + a[1][1]*a[2][2])/outDET
outINV[0][1] = (+a[0][2]*a[1][2] - a[0][1]*a[2][2])/outDET
outINV[0][2] = (-a[0][2]*a[1][1] + a[0][1]*a[1][2])/outDET
outINV[1][1] = (-a[0][2]**2 + a[0][0]*a[2][2])/outDET
outINV[1][2] = (+a[0][1]*a[0][2] - a[0][0]*a[1][2])/outDET
outINV[2][2] = (-a[0][1]**2 + a[0][0]*a[1][1])/outDET
outINV[1][0] = outINV[0][1]
outINV[2][0] = outINV[0][2]
outINV[2][1] = outINV[1][2]
return outINV, outDET
def symm_matrix_inverter4x4(a):
# It is far more efficient to write out the matrix determinant and inverse by hand
# instead of using symmetry_optionPy's built-in functions, since the matrix is symmetric.
outDET = + a[0][2]*a[0][2]*a[1][3]*a[1][3] + a[0][3]*a[0][3]*a[1][2]*a[1][2] + a[0][1]*a[0][1]*a[2][3]*a[2][3] \
- a[0][0]*a[1][3]*a[1][3]*a[2][2] - a[0][3]*a[0][3]*a[1][1]*a[2][2] - a[0][0]*a[1][1]*a[2][3]*a[2][3] \
- 2*(+ a[0][1]*a[0][2]*a[1][3]*a[2][3] - a[0][0]*a[1][2]*a[1][3]*a[2][3] \
- a[0][3]*(- a[0][2]*a[1][2]*a[1][3] + a[0][1]*a[1][3]*a[2][2] \
+ a[0][2]*a[1][1]*a[2][3] - a[0][1]*a[1][2]*a[2][3])) \
- a[3][3] * (+ a[0][2]*a[0][2]*a[1][1] - a[0][1]*a[0][2]*a[1][2] - a[0][1]*a[0][2]*a[1][2] \
+ a[0][0]*a[1][2]*a[1][2] + a[0][1]*a[0][1]*a[2][2] - a[0][0]*a[1][1]*a[2][2])
outINV = [[sp.sympify(0) for i in range(4)] for j in range(4)]
# First fill in the upper-triangle of the gPhysINV matrix...
outINV[0][0] = (-a[1][3]*a[1][3]*a[2][2] + 2*a[1][2]*a[1][3]*a[2][3] - a[1][1]*a[2][3]*a[2][3] - a[1][2]*a[1][2]*a[3][3] + a[1][1]*a[2][2]*a[3][3])/outDET
outINV[1][1] = (-a[0][3]*a[0][3]*a[2][2] + 2*a[0][2]*a[0][3]*a[2][3] - a[0][0]*a[2][3]*a[2][3] - a[0][2]*a[0][2]*a[3][3] + a[0][0]*a[2][2]*a[3][3])/outDET
outINV[2][2] = (-a[0][3]*a[0][3]*a[1][1] + 2*a[0][1]*a[0][3]*a[1][3] - a[0][0]*a[1][3]*a[1][3] - a[0][1]*a[0][1]*a[3][3] + a[0][0]*a[1][1]*a[3][3])/outDET
outINV[3][3] = (-a[0][2]*a[0][2]*a[1][1] + 2*a[0][1]*a[0][2]*a[1][2] - a[0][0]*a[1][2]*a[1][2] - a[0][1]*a[0][1]*a[2][2] + a[0][0]*a[1][1]*a[2][2])/outDET
outINV[0][1] = (+a[0][3]*a[1][3]*a[2][2] - a[0][3]*a[1][2]*a[2][3] - a[0][2]*a[1][3]*a[2][3] + a[0][1]*a[2][3]*a[2][3] + a[0][2]*a[1][2]*a[3][3] - a[0][1]*a[2][2]*a[3][3])/outDET
outINV[0][2] = (-a[0][3]*a[1][2]*a[1][3] + a[0][2]*a[1][3]*a[1][3] + a[0][3]*a[1][1]*a[2][3] - a[0][1]*a[1][3]*a[2][3] - a[0][2]*a[1][1]*a[3][3] + a[0][1]*a[1][2]*a[3][3])/outDET
outINV[0][3] = (-a[0][2]*a[1][2]*a[1][3] + a[0][1]*a[1][3]*a[2][2] + a[0][3]*a[1][2]*a[1][2] - a[0][3]*a[1][1]*a[2][2] + a[0][2]*a[1][1]*a[2][3] - a[0][1]*a[1][2]*a[2][3])/outDET
outINV[1][2] = (+a[0][3]*a[0][3]*a[1][2] + a[0][0]*a[1][3]*a[2][3] - a[0][3]*a[0][2]*a[1][3] - a[0][3]*a[0][1]*a[2][3] + a[0][1]*a[0][2]*a[3][3] - a[0][0]*a[1][2]*a[3][3])/outDET
outINV[1][3] = (+a[0][2]*a[0][2]*a[1][3] + a[0][1]*a[0][3]*a[2][2] - a[0][0]*a[1][3]*a[2][2] + a[0][0]*a[1][2]*a[2][3] - a[0][2]*a[0][3]*a[1][2] - a[0][2]*a[0][1]*a[2][3])/outDET
outINV[2][3] = (+a[0][2]*a[0][3]*a[1][1] - a[0][1]*a[0][3]*a[1][2] - a[0][1]*a[0][2]*a[1][3] + a[0][0]*a[1][2]*a[1][3] + a[0][1]*a[0][1]*a[2][3] - a[0][0]*a[1][1]*a[2][3])/outDET
# Then we fill the lower triangle of the symmetric matrix
outINV[1][0] = outINV[0][1]
outINV[2][0] = outINV[0][2]
outINV[2][1] = outINV[1][2]
outINV[3][0] = outINV[0][3]
outINV[3][1] = outINV[1][3]
outINV[3][2] = outINV[2][3]
return outINV, outDET
# symmetry_optionPy's generic matrix inverter is highly inefficient for 3x3 matrices, so here we have an optimized version.
def generic_matrix_inverter3x3(a):
outDET = -a[0][2]*a[1][1]*a[2][0] + a[0][1]*a[1][2]*a[2][0] + \
a[0][2]*a[1][0]*a[2][1] - a[0][0]*a[1][2]*a[2][1] - \
a[0][1]*a[1][0]*a[2][2] + a[0][0]*a[1][1]*a[2][2]
outINV = [[sp.sympify(0) for i in range(3)] for j in range(3)]
outINV[0][0] = -a[1][2]*a[2][1] + a[1][1]*a[2][2]
outINV[0][1] = a[0][2]*a[2][1] - a[0][1]*a[2][2]
outINV[0][2] = -a[0][2]*a[1][1] + a[0][1]*a[1][2]
outINV[1][0] = a[1][2]*a[2][0] - a[1][0]*a[2][2]
outINV[1][1] = -a[0][2]*a[2][0] + a[0][0]*a[2][2]
outINV[1][2] = a[0][2]*a[1][0] - a[0][0]*a[1][2]
outINV[2][0] = -a[1][1]*a[2][0] + a[1][0]*a[2][1]
outINV[2][1] = a[0][1]*a[2][0] - a[0][0]*a[2][1]
outINV[2][2] = -a[0][1]*a[1][0] + a[0][0]*a[1][1]
for i in range(3):
for j in range(3):
outINV[i][j] /= outDET
return outINV, outDET
def generic_matrix_inverter4x4(a):
# A = {{a00, a01, a02, a03},
# {a10, a11, a12, a13},
# {a20, a21, a22, a23},
# {a30, a31, a32, a33}}
# A // MatrixForm
# CForm[FullSimplify[Det[A]]] >>> t2.txt
# cat t2.txt | sed "s/ //g" |sed "s/ //g;s/\([0-3]\)/[\1]/g"
outDET = a[0][1]*a[1][3]*a[2][2]*a[3][0]-a[0][1]*a[1][2]*a[2][3]*a[3][0]-a[0][0]*a[1][3]*a[2][2]*a[3][1]+ \
a[0][0]*a[1][2]*a[2][3]*a[3][1]-a[0][1]*a[1][3]*a[2][0]*a[3][2]+a[0][0]*a[1][3]*a[2][1]*a[3][2]+ \
a[0][1]*a[1][0]*a[2][3]*a[3][2]-a[0][0]*a[1][1]*a[2][3]*a[3][2]+ \
a[0][3]*(a[1][2]*a[2][1]*a[3][0]-a[1][1]*a[2][2]*a[3][0]-a[1][2]*a[2][0]*a[3][1]+a[1][0]*a[2][2]*a[3][1]+
a[1][1]*a[2][0]*a[3][2]-a[1][0]*a[2][1]*a[3][2])+ \
(a[0][1]*a[1][2]*a[2][0]-a[0][0]*a[1][2]*a[2][1]-a[0][1]*a[1][0]*a[2][2]+a[0][0]*a[1][1]*a[2][2])*a[3][3]+\
a[0][2]*(-(a[1][3]*a[2][1]*a[3][0])+a[1][1]*a[2][3]*a[3][0]+a[1][3]*a[2][0]*a[3][1]-a[1][0]*a[2][3]*a[3][1]-
a[1][1]*a[2][0]*a[3][3]+a[1][0]*a[2][1]*a[3][3])
outINV = [[sp.sympify(0) for i in range(4)] for j in range(4)]
# CForm[FullSimplify[Inverse[A]*Det[A]]] >>> t.txt
# cat t.txt | sed "s/,/\n/g;s/List(//g;s/))/)/g;s/)//g;s/(//g"|grep -v ^$|sed "s/ //g;s/\([0-3]\)/[\1]/g"| awk '{line[NR]=$0}END{count=1;for(i=0;i<4;i++) { for(j=0;j<4;j++) { printf "outINV[%d][%d] = %s\n", i,j,line[count];count++; }}}'
outINV[0][0] = -a[1][3]*a[2][2]*a[3][1]+a[1][2]*a[2][3]*a[3][1]+a[1][3]*a[2][1]*a[3][2]-a[1][1]*a[2][3]*a[3][2]-a[1][2]*a[2][1]*a[3][3]+a[1][1]*a[2][2]*a[3][3]
outINV[0][1] = a[0][3]*a[2][2]*a[3][1]-a[0][2]*a[2][3]*a[3][1]-a[0][3]*a[2][1]*a[3][2]+a[0][1]*a[2][3]*a[3][2]+a[0][2]*a[2][1]*a[3][3]-a[0][1]*a[2][2]*a[3][3]
outINV[0][2] = -a[0][3]*a[1][2]*a[3][1]+a[0][2]*a[1][3]*a[3][1]+a[0][3]*a[1][1]*a[3][2]-a[0][1]*a[1][3]*a[3][2]-a[0][2]*a[1][1]*a[3][3]+a[0][1]*a[1][2]*a[3][3]
outINV[0][3] = a[0][3]*a[1][2]*a[2][1]-a[0][2]*a[1][3]*a[2][1]-a[0][3]*a[1][1]*a[2][2]+a[0][1]*a[1][3]*a[2][2]+a[0][2]*a[1][1]*a[2][3]-a[0][1]*a[1][2]*a[2][3]
outINV[1][0] = a[1][3]*a[2][2]*a[3][0]-a[1][2]*a[2][3]*a[3][0]-a[1][3]*a[2][0]*a[3][2]+a[1][0]*a[2][3]*a[3][2]+a[1][2]*a[2][0]*a[3][3]-a[1][0]*a[2][2]*a[3][3]
outINV[1][1] = -a[0][3]*a[2][2]*a[3][0]+a[0][2]*a[2][3]*a[3][0]+a[0][3]*a[2][0]*a[3][2]-a[0][0]*a[2][3]*a[3][2]-a[0][2]*a[2][0]*a[3][3]+a[0][0]*a[2][2]*a[3][3]
outINV[1][2] = a[0][3]*a[1][2]*a[3][0]-a[0][2]*a[1][3]*a[3][0]-a[0][3]*a[1][0]*a[3][2]+a[0][0]*a[1][3]*a[3][2]+a[0][2]*a[1][0]*a[3][3]-a[0][0]*a[1][2]*a[3][3]
outINV[1][3] = -a[0][3]*a[1][2]*a[2][0]+a[0][2]*a[1][3]*a[2][0]+a[0][3]*a[1][0]*a[2][2]-a[0][0]*a[1][3]*a[2][2]-a[0][2]*a[1][0]*a[2][3]+a[0][0]*a[1][2]*a[2][3]
outINV[2][0] = -a[1][3]*a[2][1]*a[3][0]+a[1][1]*a[2][3]*a[3][0]+a[1][3]*a[2][0]*a[3][1]-a[1][0]*a[2][3]*a[3][1]-a[1][1]*a[2][0]*a[3][3]+a[1][0]*a[2][1]*a[3][3]
outINV[2][1] = a[0][3]*a[2][1]*a[3][0]-a[0][1]*a[2][3]*a[3][0]-a[0][3]*a[2][0]*a[3][1]+a[0][0]*a[2][3]*a[3][1]+a[0][1]*a[2][0]*a[3][3]-a[0][0]*a[2][1]*a[3][3]
outINV[2][2] = -a[0][3]*a[1][1]*a[3][0]+a[0][1]*a[1][3]*a[3][0]+a[0][3]*a[1][0]*a[3][1]-a[0][0]*a[1][3]*a[3][1]-a[0][1]*a[1][0]*a[3][3]+a[0][0]*a[1][1]*a[3][3]
outINV[2][3] = a[0][3]*a[1][1]*a[2][0]-a[0][1]*a[1][3]*a[2][0]-a[0][3]*a[1][0]*a[2][1]+a[0][0]*a[1][3]*a[2][1]+a[0][1]*a[1][0]*a[2][3]-a[0][0]*a[1][1]*a[2][3]
outINV[3][0] = a[1][2]*a[2][1]*a[3][0]-a[1][1]*a[2][2]*a[3][0]-a[1][2]*a[2][0]*a[3][1]+a[1][0]*a[2][2]*a[3][1]+a[1][1]*a[2][0]*a[3][2]-a[1][0]*a[2][1]*a[3][2]
outINV[3][1] = -a[0][2]*a[2][1]*a[3][0]+a[0][1]*a[2][2]*a[3][0]+a[0][2]*a[2][0]*a[3][1]-a[0][0]*a[2][2]*a[3][1]-a[0][1]*a[2][0]*a[3][2]+a[0][0]*a[2][1]*a[3][2]
outINV[3][2] = a[0][2]*a[1][1]*a[3][0]-a[0][1]*a[1][2]*a[3][0]-a[0][2]*a[1][0]*a[3][1]+a[0][0]*a[1][2]*a[3][1]+a[0][1]*a[1][0]*a[3][2]-a[0][0]*a[1][1]*a[3][2]
outINV[3][3] = -a[0][2]*a[1][1]*a[2][0]+a[0][1]*a[1][2]*a[2][0]+a[0][2]*a[1][0]*a[2][1]-a[0][0]*a[1][2]*a[2][1]-a[0][1]*a[1][0]*a[2][2]+a[0][0]*a[1][1]*a[2][2]
for mu in range(4):
for nu in range(4):
outINV[mu][nu] /= outDET
return outINV, outDET
# This module contains infrastructure for generating
# C-code loops of arbitrary dimension
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sys # Standard Python modules for multiplatform OS-level functions
# loop1D() is just a special case of loop(), and in fact is called by loop().
# For documentation on the inputs, see loop()'s documentation below.
def loop1D(idxvar="i0",lower="0",upper="Nx0",incr="1",OpenMPpragma="#pragma omp parallel for",tabprefix=""):
if not (isinstance(idxvar, str) and isinstance(lower, str) and
isinstance(upper, str) and isinstance(incr, str) and isinstance(OpenMPpragma, str)):
print("Error: all inputs to loop1D() must be STRINGS, and to loop() must be LISTS OF STRINGS")
sys.exit(1)
OMPheader = ""
if OpenMPpragma != "":
OMPheader = OpenMPpragma + "\n"
incrstr = "++"
if incr != "1":
incrstr = "+="+incr
loopbody = tabprefix+"for(int "+idxvar+"="+lower+"; "+idxvar+"<"+upper+"; "+idxvar+incrstr+")"
return OMPheader+loopbody+" {\n", tabprefix+"} // END LOOP: "+loopbody.replace(tabprefix,"")+"\n"
# loop() creates a C code loop, taking as input:
# idxvar (string or list of strings): the index variable name or list of names.
# In the case that idxvar is a list of N strings, we adopt the formulation:
# idxvar[0]=outermost loop variable
# idxvar[N-1]=innermost loop variable
# lower (string or list of strings): lower loop index of idxvar or idxvar[i].
# See definition of "idxvar" if lower is a list of strings.
# idxvar[] must have the same length as idxvar.
# upper (string or list of strings): Defined similarly to "lower", except
# this refers to the *upper* loop index of idxvar or idxvar[i].
# incr (string or list of strings): Defined similarly to "lower", except
# this refers to the loop increment of idxvar or idxvar[i]
# (incr[i] = 1 -> idxvar++)
# OpenMPpragma (string or list of strings): Defined similarly to "lower", except
# this refers to the OpenMP pragma corresponding to idxvar or idxvar[i].
# tabprefix (optional; string): Sets the tab stop for the C code.
# loopguts (optional; string): The loop interior.
#
# Example: loop(["i0","i1"],["0","5"],["N","N-5"],["1","5"],["#pragma omp parallel for",""],
# "double a=-2;\n gf[IDX3(GF,i0,i1)]=-2*a;\n"])
# Output:
# #pragma omp parallel for
# for(int i0=0;i0<N;i0++) {
# for(int i1=5;i1<N-5;i1+=5) {
# a=-2;
# gf[IDX3(GF,i0,i1)]=-2*a;
# } // END LOOP: for(int i1=5;i1<N-5;i1+=5)
# } // END LOOP: for(int i0=0;i0<N;i0++)
def loop(idxvar,lower,upper,incr,OpenMPpragma,tabprefix="",loopguts=""):
# Step 1: Check and/or clean input.
# Step 1a: If only strings are passed, then create lists out of them:
if (isinstance(idxvar,str) and isinstance(lower,str) and
isinstance(upper, str) and isinstance(incr, str) and isinstance(OpenMPpragma, str)):
idxvar = [idxvar]
lower = [lower]
upper = [upper]
incr = [incr]
OpenMPpragma = [OpenMPpragma]
# Step 1b: At this point all inputs should be lists. If they are not, then exit.
if not (isinstance(idxvar,list) and isinstance(lower,list) and
isinstance(upper, list) and isinstance(incr, list) and isinstance(OpenMPpragma, list)):
print("Error: loop(idxvar,lower,upper,incr,OpenMPpragma) requires all inputs be lists")
sys.exit(1)
# Step 1c: At this point all inputs should be lists. If they are not the same length, then exit.
if len(idxvar) != len(lower) or len(lower) != len(upper) or len(upper) != len(incr) or len(incr) != len(
OpenMPpragma):
print("Error: loop(idxvar,lower,upper,incr,OpenMPpragma) requires all inputs be lists OF THE SAME LENGTH")
sys.exit(1)
# Step 2: tabprefix will be set according to the loop nesting, so the loop has proper tabination;
# one tab for each nesting of the loop.
# Step 3: header will be the top of the loop
header = ""
# Step 4: footerarray
footerarray = []
for i in range(len(idxvar)):
headerstr,footerstr = loop1D(idxvar[i],lower[i],upper[i],incr[i],OpenMPpragma[i],tabprefix)
header += headerstr
footerarray.append(footerstr)
tabprefix += " "
loopgutsout = ""
if loopguts != "":
loopgutsarray = loopguts.split("\n")
for line in loopgutsarray:
loopgutsout += tabprefix+line+"\n"
footer = ""
for i in range(len(idxvar)-1,-1,-1):
footer += footerarray[i]
if loopguts == "":
return header,footer
return header+loopgutsout+footer
# Automatic generation of C-code loops around an arbitrarily
# defined loop body.
def simple_loop(loopopts, body):
if loopopts == "":
return body
if "AllPoints" in loopopts:
i2i1i0_mins = ["0", "0", "0"]
i2i1i0_maxs = ["Nxx_plus_2NGHOSTS2", "Nxx_plus_2NGHOSTS1", "Nxx_plus_2NGHOSTS0"]
if "oldloops" in loopopts:
i2i1i0_maxs = ["Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]"]
elif "InteriorPoints" in loopopts:
i2i1i0_mins = ["NGHOSTS","NGHOSTS","NGHOSTS"]
i2i1i0_maxs = ["NGHOSTS+Nxx2","NGHOSTS+Nxx1","NGHOSTS+Nxx0"]
if "oldloops" in loopopts:
i2i1i0_maxs = ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"]
else:
print("Error: loopopts given, but no points over which to loop were specified.")
sys.exit(1)
Read_1Darrays = ["", "", ""]
if "Read_xxs" in loopopts:
if not "EnableSIMD" in loopopts:
Read_1Darrays = ["const REAL xx0 = xx[0][i0];",
" const REAL xx1 = xx[1][i1];",
" const REAL xx2 = xx[2][i2];", ]
else:
print("Error: No SIMD support on Read_xxs yet.")
sys.exit(1)
if "Enable_rfm_precompute" in loopopts:
if "Read_xxs" in loopopts:
print("Error: Enable_rfm_precompute and Read_xxs cannot both be enabled.")
sys.exit(1)
if "EnableSIMD" in loopopts:
Read_1Darrays = ["#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"",
"#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\"",
"#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\""]
else:
Read_1Darrays = ["#include \"rfm_files/rfm_struct__read0.h\"",
"#include \"rfm_files/rfm_struct__read1.h\"",
"#include \"rfm_files/rfm_struct__read2.h\""]
OpenMPpragma = "#pragma omp parallel for"
if "DisableOpenMP" in loopopts:
OpenMPpragma = ""
loopincrements = ["1","1","1"]
if "EnableSIMD" in loopopts:
loopincrements = ["1", "1", "SIMD_width"]
return loop(["i2","i1","i0"],i2i1i0_mins,i2i1i0_maxs,loopincrements,
[OpenMPpragma,Read_1Darrays[2],Read_1Darrays[1]],tabprefix=" ",loopguts = Read_1Darrays[0]+"\n"+body)
#loopheader,loopfooter = loop(idxvar=["i0","i1"],lower=["0","0"],upper=["Nx0","Nx1"],incr=["1","1"],
# OpenMPpragma=["", "#pragma omp parallel for"])
#loopheader,loopfooter = loop(idxvar=["i0","i1"],lower=["0","0"],upper=["Nx0","Nx1"],incr=["1","1"],
# OpenMPpragma=["", "#pragma omp parallel for"])
#print(loopheader+loopfooter)
# As documented in the NRPy+ tutorial module
# Tutorial-Coutput__Parameter_Interface.ipynb
# this core NRPy+ module is used for
# generating C code and functions.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import loop as lp # NRPy+: C code loop interface
import NRPy_param_funcs as par # NRPy+: parameter interface
from SIMD import expr_convert_to_SIMD_intrins # NRPy+: SymPy expression => SIMD intrinsics interface
import sympy as sp # Import SymPy
import re, sys # Standard Python: regular expressions & OS-independent system functions
from collections import namedtuple # Standard Python: Enable namedtuple data type
lhrh = namedtuple('lhrh', 'lhs rhs')
outCparams = namedtuple('outCparams', 'preindent includebraces declareoutputvars outCfileaccess outCverbose CSE_enable CSE_varprefix SIMD_enable SIMD_const_suffix SIMD_debug enable_TYPE')
# Sometimes SymPy has problems evaluating complicated expressions involving absolute
# values, resulting in hangs. So instead of using sp.Abs(), if we instead use
# nrpyAbs, we can sidestep the internal SymPy evaluation and force the C
# codegen to output our desired fabs().
nrpyAbs = sp.Function('nrpyAbs')
custom_functions_for_SymPy_ccode = {
"nrpyAbs": "fabs",
'Pow': [(lambda b, e: e == 0.5, lambda b, e: 'sqrt(%s)' % (b)),
(lambda b, e: e ==-0.5, lambda b, e: '(1.0/sqrt(%s))' % (b)),
(lambda b, e: e == sp.S.One/3, lambda b, e: 'cbrt(%s)' % (b)),
(lambda b, e: e ==-sp.S.One/3, lambda b, e: '(1.0/cbrt(%s))' % (b)),
(lambda b, e: e == 2, lambda b, e: '((%s)*(%s))' % (b,b)),
(lambda b, e: e == 3, lambda b, e: '((%s)*(%s)*(%s))' % (b,b,b)),
(lambda b, e: e == 4, lambda b, e: '((%s)*(%s)*(%s)*(%s))' % (b,b,b,b)),
(lambda b, e: e == 5, lambda b, e: '((%s)*(%s)*(%s)*(%s)*(%s))' % (b,b,b,b,b)),
(lambda b, e: e ==-1, lambda b, e: '(1.0/(%s))' % (b)),
(lambda b, e: e ==-2, lambda b, e: '(1.0/((%s)*(%s)))' % (b,b)),
(lambda b, e: e ==-3, lambda b, e: '(1.0/((%s)*(%s)*(%s)))' % (b,b,b)),
(lambda b, e: e ==-4, lambda b, e: '(1.0/((%s)*(%s)*(%s)*(%s)))' % (b,b,b,b)),
(lambda b, e: e ==-5, lambda b, e: '(1.0/((%s)*(%s)*(%s)*(%s)*(%s)))' % (b,b,b,b,b)),
(lambda b, e: e !=-5, 'pow')]
## (lambda b, e: e != 2, 'pow')]
}
# Parameter initialization is called once, within nrpy.py.
par.initialize_param(par.glb_param("char", __name__, "PRECISION", "double")) # __name__ = "outputC", this module's name.
# par.initialize_param(par.glb_param("bool", thismodule, "SIMD_enable", False))
# super fast 'uniq' function:
# f8() function from https://www.peterbe.com/plog/uniqifiers-benchmark
def superfast_uniq(seq): # Author: Dave Kirby
# Order preserving
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
def check_if_string__error_if_not(allegedstring,stringdesc):
import sys
if sys.version_info[0] == 3:
string_types = str
else:
string_types = basestring
if not isinstance(allegedstring, string_types):
print("ERROR: "+str(stringdesc)+" =="+str(allegedstring)+" not a string!")
sys.exit(1)
def ccode_postproc(string):
PRECISION = par.parval_from_str("PRECISION")
# In the C math library, e.g., pow(x,y) assumes x and y are doubles, and returns a double.
# If x and y are floats, then for consistency should use powf(x,y) instead.
# Similarly, in the case of x and y being long doubles, should use powl(x,y) for consistency.
# First we find the appropriate suffix depending on the desired precision:
cmathsuffix = ""
if PRECISION == "double":
pass
elif PRECISION == "long double":
cmathsuffix = "l"
elif PRECISION == "float":
cmathsuffix = "f"
else:
print("Error: "+__name__+"::PRECISION = \""+ PRECISION +"\" not supported")
sys.exit(1)
# ... then we append the above suffix to standard C math library functions:
for func in ['pow', 'sqrt', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'exp', 'log', 'fabs']:
string2 = re.sub(func+r'\(', func + cmathsuffix+"(", string); string = string2
# Finally, SymPy prefers to output Rationals as long-double fractions.
# E.g., Rational(1,3) is output as 1.0L/3.0L.
# The Intel compiler vectorizer complains miserably about this,
# and strictly speaking it is useless when we're in double precision.
# So here we get rid of the "L" suffix on floating point numbers:
if PRECISION!="long double":
string2 = re.sub(r'([0-9.]+)L/([0-9.]+)L', '(\\1 / \\2)', string); string = string2
return string
def parse_outCparams_string(params):
# Default values:
preindent = ""
includebraces = "True"
declareoutputvars = "False"
outCfileaccess = "w"
outCverbose = "True"
CSE_enable = "True"
CSE_varprefix = "tmp"
SIMD_enable = "False"
SIMD_const_suffix = ""
SIMD_debug = "False"
enable_TYPE = "True"
if params != "":
params2 = re.sub("^,","",params)
params = params2.strip()
splitstring = re.split("=|,", params)
if len(splitstring) % 2 != 0:
print("outputC: Invalid params string: "+params)
sys.exit(1)
parnm = []
value = []
for i in range(int(len(splitstring)/2)):
parnm.append(splitstring[2*i])
value.append(splitstring[2*i+1])
for i in range(len(parnm)):
# Clean the string
if value[i] == "true":
value[i] = "True"
if value[i] == "false":
value[i] = "False"
if parnm[i] == "preindent":
if not value[i].isdigit():
print("Error: preindent must be set to an integer (corresponding to the number of tab stops). ")
print(value[i]+" is not an integer.")
sys.exit(1)
preindent = ""
for i in range(int(value[i])):
preindent += " "
elif parnm[i] == "includebraces":
includebraces = value[i]
elif parnm[i] == "declareoutputvars":
declareoutputvars = value[i]
elif parnm[i] == "outCfileaccess":
outCfileaccess = value[i]
elif parnm[i] == "outCverbose":
outCverbose = value[i]
elif parnm[i] == "CSE_enable":
CSE_enable = value[i]
elif parnm[i] == "CSE_varprefix":
CSE_varprefix = value[i]
elif parnm[i] == "SIMD_enable":
SIMD_enable = value[i]
elif parnm[i] == "SIMD_const_suffix":
SIMD_const_suffix = value[i]
elif parnm[i] == "SIMD_debug":
SIMD_debug = value[i]
elif parnm[i] == "enable_TYPE":
enable_TYPE = value[i]
else:
print("Error: outputC parameter name \""+parnm[i]+"\" unrecognized.")
sys.exit(1)
return outCparams(preindent,includebraces,declareoutputvars,outCfileaccess,outCverbose,CSE_enable,CSE_varprefix,SIMD_enable,SIMD_const_suffix,SIMD_debug,enable_TYPE)
import sympy as sp
# Input: sympyexpr = a single SymPy expression *or* a list of SymPy expressions
# output_varname_str = a single output variable name *or* a list of output
# variable names, one per sympyexpr.
# Output: C code, as a string.
def outputC(sympyexpr, output_varname_str, filename = "stdout", params = "", prestring = "", poststring = ""):
outCparams = parse_outCparams_string(params)
preindent = outCparams.preindent
TYPE = par.parval_from_str("PRECISION")
if outCparams.enable_TYPE == "False":
TYPE = ""
# Step 0: Initialize
# commentblock: comment block containing the input SymPy string,
# set only if outCverbose==True
# outstring: the output C code string
commentblock = ""
outstring = ""
# Step 1: If SIMD_enable==True, then check if TYPE=="double". If not, error out.
# Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd
# within the C code. For example for AVX-256, the C code should have
# #define REAL_SIMD_ARRAY __m256d
if outCparams.SIMD_enable == "True":
if not (TYPE == "double" or TYPE == ""):
print("SIMD output currently only supports double precision or typeless. Sorry!")
sys.exit(1)
if TYPE == "double":
TYPE = "REAL_SIMD_ARRAY"
else:
TYPE = ""
# Step 2a: Apply sanity checks when either sympyexpr or
# output_varname_str is a list.
if type(output_varname_str) is list and type(sympyexpr) is not list:
print("Error: Provided a list of output variable names, but only one SymPy expression.")
sys.exit(1)
if type(sympyexpr) is list:
if type(output_varname_str) is not list:
print("Error: Provided a list of SymPy expressions, but no corresponding list of output variable names")
sys.exit(1)
elif len(output_varname_str) != len(sympyexpr):
print("Error: Length of SymPy expressions list ("+str(len(sympyexpr))+
") != Length of corresponding output variable name list ("+str(len(output_varname_str))+")")
sys.exit(1)
# Step 2b: If sympyexpr and output_varname_str are not lists,
# convert them to lists of one element each, to
# simplify proceeding code.
if type(output_varname_str) is not list and type(sympyexpr) is not list:
output_varname_strtmp = [output_varname_str]
output_varname_str = output_varname_strtmp
sympyexprtmp = [sympyexpr]
sympyexpr = sympyexprtmp
# Step 3: If outCparams.verbose = True, then output the original SymPy
# expression(s) in code comments prior to actual C code
if outCparams.outCverbose == "True":
commentblock += preindent+"/*\n"+preindent+" * Original SymPy expression"
if len(output_varname_str)>1:
commentblock += "s"
commentblock += ":\n"
for i in range(len(output_varname_str)):
if i==0:
if len(output_varname_str)!=1:
commentblock += preindent+" * \"["
else:
commentblock += preindent+" * \""
else:
commentblock += preindent+" * "
commentblock += output_varname_str[i] + " = " + str(sympyexpr[i])
if i==len(output_varname_str)-1:
if len(output_varname_str)!=1:
commentblock += "]\"\n"
else:
commentblock += "\"\n"
else:
commentblock += ",\n"
commentblock += preindent+" */\n"
# Step 4: Add proper indentation of C code:
if outCparams.includebraces == "True":
indent = outCparams.preindent+" "
else:
indent = outCparams.preindent+""
# Step 5: Should the output variable, e.g., outvar, be declared?
# If so, start output line with e.g., "double outvar "
outtypestring = ""
if outCparams.declareoutputvars == "True":
outtypestring = outCparams.preindent+indent+TYPE + " "
else:
outtypestring = outCparams.preindent+indent
# Step 6a: If common subexpression elimination (CSE) disabled, then
# just output the SymPy string in the most boring way,
# nearly consistent with SymPy's ccode() function,
# though with support for float & long double types
# as well.
SIMD_decls = ""
if outCparams.CSE_enable == "False":
# If CSE is disabled:
for i in range(len(sympyexpr)):
outstring += outtypestring + ccode_postproc(sp.ccode(sympyexpr[i], output_varname_str[i],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
# Step 6b: If CSE enabled, then perform CSE using SymPy and then
# resulting C code.
else:
# If CSE is enabled:
SIMD_const_varnms = []
SIMD_const_values = []
CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix), order='canonical')
for commonsubexpression in CSE_results[0]:
FULLTYPESTRING = "const " + TYPE + " "
if outCparams.enable_TYPE == "False":
FULLTYPESTRING = ""
if outCparams.SIMD_enable == "True":
outstring += outCparams.preindent + indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \
str(expr_convert_to_SIMD_intrins(commonsubexpression[1],SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_const_suffix,outCparams.SIMD_debug)) + ";\n"
else:
outstring += outCparams.preindent+indent+FULLTYPESTRING+ccode_postproc(sp.ccode(commonsubexpression[1],commonsubexpression[0],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
for i,result in enumerate(CSE_results[1]):
if outCparams.SIMD_enable == "True":
outstring += outtypestring + output_varname_str[i] + " = " + \
str(expr_convert_to_SIMD_intrins(result,SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_const_suffix,outCparams.SIMD_debug)) + ";\n"
else:
outstring += outtypestring+ccode_postproc(sp.ccode(result,output_varname_str[i],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
# Step 6b.i: If SIMD_enable == True , and
# there is at least one SIMD const variable,
# then declare the SIMD_const_varnms and SIMD_const_values arrays
if outCparams.SIMD_enable == "True" and len(SIMD_const_varnms) != 0:
# Step 6a) Sort the list of definitions. Idea from:
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
SIMD_const_varnms, SIMD_const_values = \
(list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values))))
# Step 6b) Remove duplicates
uniq_varnms = superfast_uniq(SIMD_const_varnms)
uniq_values = superfast_uniq(SIMD_const_values)
SIMD_const_varnms = uniq_varnms
SIMD_const_values = uniq_values
if len(SIMD_const_varnms) != len(SIMD_const_values):
print("Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!")
sys.exit(1)
for i in range(len(SIMD_const_varnms)):
if outCparams.enable_TYPE == "False":
SIMD_decls += outCparams.preindent + indent + SIMD_const_varnms[i] + " = " + SIMD_const_values[i]+";"
else:
SIMD_decls += outCparams.preindent + indent + "const double " + outCparams.CSE_varprefix + SIMD_const_varnms[i] + " = " + SIMD_const_values[i] + ";\n"
SIMD_decls += outCparams.preindent+indent+ "const REAL_SIMD_ARRAY " + SIMD_const_varnms[i] + " = ConstSIMD("+ outCparams.CSE_varprefix + SIMD_const_varnms[i] + ");\n"
SIMD_decls += "\n"
# Step 7: Construct final output string
final_Ccode_output_str = commentblock
# Step 7a: Output C code in indented curly brackets if
# outCparams.includebraces = True
if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"{\n"
final_Ccode_output_str += prestring + SIMD_decls + outstring + poststring
if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"}\n"
# Step 8: If filename == "stdout", then output
# C code to standard out (useful for copy-paste or interactive
# mode). Otherwise output to file specified in variable name.
if filename == "stdout":
# Output to standard out (stdout; "the screen")
print(final_Ccode_output_str)
elif filename == "returnstring":
return final_Ccode_output_str
else:
# Output to the file specified by the function input parameter string 'filename':
with open(filename, outCparams.outCfileaccess) as file:
file.write(final_Ccode_output_str)
successstr = ""
if outCparams.outCfileaccess == "a":
successstr = "Appended "
elif outCparams.outCfileaccess == "w":
successstr = "Wrote "
print(successstr + "to file \"" + filename + "\"")
outC_function_prototype_dict = {}
outC_function_dict = {}
def Cfunction(desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
if name == None or params == None or body == None:
print("Cfunction() error: strings must be provided for function name, parameters, and body")
sys.exit(1)
func_prototype = type+" "+name+"("+params+")"
include_Cparams_str = ""
if not "DisableCparameters" in opts:
if "EnableSIMD" in loopopts:
include_Cparams_str = "#include \"set_Cparameters-SIMD.h\"\n"
else:
include_Cparams_str = "#include \"set_Cparameters.h\"\n"
complete_func = ""
if desc != "":
complete_func = "/*\n" + desc + "\n */\n"
complete_func += func_prototype + " {\n"+include_Cparams_str+preloop+"\n"+lp.simple_loop(loopopts,body)+postloop+"}\n"
return func_prototype+";",complete_func
def add_to_Cfunction_dict(desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
outC_function_prototype_dict[name],outC_function_dict[name] = Cfunction(desc,type,name,params,preloop,body,loopopts,postloop,opts)
def outCfunction(outfile="",desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
ignoreprototype,Cfunc = Cfunction(desc,type,name,params,preloop,body,loopopts,postloop,opts)
if outfile == "returnstring":
return Cfunc
with open(outfile,"w") as file:
file.write(Cfunc)
print("Output C function "+name+"() to file "+outfile)
# reference_metric.py: Define all needed quantities
# for a reference metric.
# Given uniform (reference metric) coordinate
# (xx[0],xx[1],xx[2]), you must define:
# 1) xxmin[3],xxmax[3]: Valid ranges for each
# uniform coordinate xx0,xx1,xx2
# 2) xxSph[3]: Spherical coordinate (r,theta,phi),
# in terms of uniform coordinate xx0,xx1,xx2
# 3) xxCart[3]: Cartesian coordinate (x,y,z),
# in terms of uniform coordinate xx0,xx1,xx2
# 4) scalefactor_orthog:
# orthogonal coordinate scale factor
# (positive root of diagonal reference metric
# components)
# 5) Cart_to_xx[3]: Inverse of xxCart:
# xx0,xx1,xx2 as functions of (x,y,z).
# In the case that there exists no closed-form
# expression, then a root finder might be needed
# 6) UnitVectors[3][3]: Unit vectors of reference
# metric.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # Import SymPy
from outputC import outputC,superfast_uniq,outC_function_dict,add_to_Cfunction_dict # NRPy+: Core C code output module
import NRPy_param_funcs as par # NRPy+: Parameter interface
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import sys # Standard Python modules for multiplatform OS-level functions
# Step 0a: Initialize parameters
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "CoordSystem", "Spherical"))
par.initialize_param(par.glb_param("char", thismodule, "enable_rfm_precompute", "False"))
par.initialize_param(par.glb_param("char", thismodule, "rfm_precompute_Ccode_outdir", "Ccode"))
# Step 0b: Declare global variables
xx = gri.xx
xxCart = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
Cart_to_xx = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
Cartx,Carty,Cartz = sp.symbols("Cartx Carty Cartz", real=True)
Cart = [Cartx,Carty,Cartz]
xxSph = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
scalefactor_orthog = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
scalefactor_orthog_funcform = ixp.zerorank1(DIM=4) # Must be set in terms of generic functions of xx[]s
have_already_called_reference_metric_function = False
def reference_metric(SymPySimplifyExpressions=True):
global f0_of_xx0_funcform, f1_of_xx1_funcform, f2_of_xx0_xx1_funcform, f3_of_xx0_funcform, f4_of_xx2_funcform
global f0_of_xx0, f1_of_xx1, f2_of_xx0_xx1, f3_of_xx0, f4_of_xx2
f0_of_xx0_funcform = sp.Function('f0_of_xx0_funcform')(xx[0])
f1_of_xx1_funcform = sp.Function('f1_of_xx1_funcform')(xx[1])
f2_of_xx0_xx1_funcform = sp.Function('f2_of_xx0_xx1_funcform')(xx[0], xx[1])
f3_of_xx0_funcform = sp.Function('f3_of_xx0_funcform')(xx[0])
f4_of_xx2_funcform = sp.Function('f4_of_xx2_funcform')(xx[2])
f0_of_xx0, f1_of_xx1, f2_of_xx0_xx1, f3_of_xx0, f4_of_xx2 = par.Cparameters("REAL", thismodule,
["f0_of_xx0", "f1_of_xx1", "f2_of_xx0_xx1", "f3_of_xx0", "f4_of_xx2"], 1e300)
# FIXME: Hack
f0_of_xx0__D0, f0_of_xx0__DD00, f0_of_xx0__DDD000 = par.Cparameters("REAL", thismodule,
["f0_of_xx0__D0", "f0_of_xx0__DD00",
"f0_of_xx0__DDD000"], 1e300)
f1_of_xx1__D1, f1_of_xx1__DD11, f1_of_xx1__DDD111 = par.Cparameters("REAL", thismodule,
["f1_of_xx1__D1", "f1_of_xx1__DD11",
"f1_of_xx1__DDD111"], 1e300)
f2_of_xx0_xx1__D0,f2_of_xx0_xx1__D1,f2_of_xx0_xx1__DD00,f2_of_xx0_xx1__DD11 = \
par.Cparameters("REAL", thismodule,
["f2_of_xx0_xx1__D0","f2_of_xx0_xx1__D1","f2_of_xx0_xx1__DD00","f2_of_xx0_xx1__DD11"],
1e300)
f3_of_xx0__D0,f3_of_xx0__DD00 = par.Cparameters("REAL", thismodule,["f3_of_xx0__D0","f3_of_xx0__DD00"], 1e300)
f4_of_xx2__D2,f4_of_xx2__DD22 = par.Cparameters("REAL", thismodule,["f4_of_xx2__D2","f4_of_xx2__DD22"], 1e300)
global have_already_called_reference_metric_function # setting to global enables other modules to see updated value.
have_already_called_reference_metric_function = True
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
M_PI,M_SQRT1_2 = par.Cparameters("#define",thismodule,["M_PI","M_SQRT1_2"],"")
global xxmin
global xxmax
global UnitVectors
UnitVectors = ixp.zerorank2(DIM=3)
# Set up hatted metric tensor, rescaling matrix, and rescaling vector
#####################################################################
# SPHERICAL-LIKE COORDINATE SYSTEMS WITH & WITHOUT RADIAL RESCALING #
#####################################################################
if CoordSystem == "Spherical" or CoordSystem == "SinhSpherical" or CoordSystem == "SinhSphericalv2":
# Adding assumption real=True can help simplify expressions involving xx[0] & xx[1] below.
xx[0] = sp.symbols("xx0", real=True)
xx[1] = sp.symbols("xx1", real=True)
if CoordSystem == "Spherical":
RMAX = par.Cparameters("REAL", thismodule, ["RMAX"],10.0)
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [ RMAX, M_PI, M_PI]
r = xx[0]
th = xx[1]
ph = xx[2]
Cart_to_xx[0] = sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)
Cart_to_xx[1] = sp.acos(Cartz / Cart_to_xx[0])
Cart_to_xx[2] = sp.atan2(Carty, Cartx)
elif CoordSystem == "SinhSpherical":
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [sp.sympify(1), M_PI, M_PI]
AMPL, SINHW = par.Cparameters("REAL",thismodule,["AMPL","SINHW"],[10.0,0.2])
# Set SinhSpherical radial coordinate by default; overwrite later if CoordSystem == "SinhSphericalv2".
r = AMPL * (sp.exp(xx[0] / SINHW) - sp.exp(-xx[0] / SINHW)) / \
(sp.exp(1 / SINHW) - sp.exp(-1 / SINHW))
th = xx[1]
ph = xx[2]
Cart_to_xx[0] = SINHW*sp.asinh(sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)*sp.sinh(1/SINHW)/AMPL)
Cart_to_xx[1] = sp.acos(Cartz / sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2))
Cart_to_xx[2] = sp.atan2(Carty, Cartx)
# SinhSphericalv2 adds the parameter "const_dr", which allows for a region near xx[0]=0 to have
# constant radial resolution of const_dr, provided the sinh() term does not dominate near xx[0]=0.
elif CoordSystem == "SinhSphericalv2":
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [sp.sympify(1), M_PI, M_PI]
AMPL, SINHW = par.Cparameters("REAL",thismodule,["AMPL","SINHW"],[10.0,0.2])
const_dr = par.Cparameters("REAL",thismodule,["const_dr"],0.0625)
r = AMPL*( const_dr*xx[0] + (sp.exp(xx[0] / SINHW) - sp.exp(-xx[0] / SINHW)) /
(sp.exp(1 / SINHW) - sp.exp(-1 / SINHW)) )
th = xx[1]
ph = xx[2]
# NO CLOSED-FORM EXPRESSION FOR RADIAL INVERSION.
# Cart_to_xx[0] = "NewtonRaphson"
# Cart_to_xx[1] = sp.acos(Cartz / sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2))
# Cart_to_xx[2] = sp.atan2(Carty, Cartx)
xxSph[0] = r
xxSph[1] = th
xxSph[2] = ph
# Now define xCart, yCart, and zCart in terms of x0,xx[1],xx[2].
# Note that the relation between r and x0 is not necessarily trivial in SinhSpherical coordinates. See above.
xxCart[0] = xxSph[0]*sp.sin(xxSph[1])*sp.cos(xxSph[2])
xxCart[1] = xxSph[0]*sp.sin(xxSph[1])*sp.sin(xxSph[2])
xxCart[2] = xxSph[0]*sp.cos(xxSph[1])
scalefactor_orthog[0] = sp.diff(xxSph[0],xx[0])
scalefactor_orthog[1] = xxSph[0]
scalefactor_orthog[2] = xxSph[0]*sp.sin(xxSph[1])
f0_of_xx0 = xxSph[0]
f1_of_xx1 = sp.sin(xxSph[1])
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0])
scalefactor_orthog_funcform[1] = f0_of_xx0_funcform
scalefactor_orthog_funcform[2] = f0_of_xx0_funcform*f1_of_xx1_funcform
# Set the unit vectors
UnitVectors = [[ sp.sin(xxSph[1])*sp.cos(xxSph[2]), sp.sin(xxSph[1])*sp.sin(xxSph[2]), sp.cos(xxSph[1])],
[ sp.cos(xxSph[1])*sp.cos(xxSph[2]), sp.cos(xxSph[1])*sp.sin(xxSph[2]), -sp.sin(xxSph[1])],
[ -sp.sin(xxSph[2]), sp.cos(xxSph[2]), sp.sympify(0) ]]
######################################################################
# SPHERICAL-LIKE COORDINATE SYSTEMS WITH RADIAL AND THETA RESCALINGS #
######################################################################
elif CoordSystem == "NobleSphericalThetaOptionOne" or CoordSystem == "NobleSphericalThetaOptionTwo":
# WARNING: CANNOT BE USED FOR SENR RUNS;
# THESE DO NOT DEFINE xxmin, xxmax, Cart_to_xx
# ALSO THE RADIAL RESCALINGS ARE NOT ODD FUNCTIONS OF xx0,
# MEANING THAT CURVI. BOUNDARY CONDITIONS WILL NOT WORK.
Rin,R0 = par.Cparameters("REAL", thismodule, ["Rin","R0"],[1.08986052555408,0.0])
x0beg = sp.log(Rin-R0)
xx[0] = sp.symbols("xx0", real=True)
r = R0 + sp.exp(x0beg + xx[0])
# 0.053407075111026485 == 0.017*pi
th_c,xi,x1beg = par.Cparameters("REAL", thismodule, ["th_c","xi","x1beg"],[0.053407075111026485,0.25,0.0])
xx[1] = sp.symbols("xx1", real=True)
x1j = x1beg + xx[1]
if CoordSystem == "NobleSphericalThetaOptionOne":
th = th_c + (M_PI - 2*th_c)*x1j + xi*sp.sin(2*M_PI*x1j)
elif CoordSystem == "NobleSphericalThetaOptionTwo":
x1_n_exponent = par.Cparameters("REAL", thismodule, ["x1_n_exponent"],9.0)
th = M_PI/2 * ( 1 + (1 - xi)*(2*x1j - 1) + (xi - 2*th_c/M_PI)*(2*x1j - 1)**x1_n_exponent )
xx[2] = sp.symbols("xx2", real=True)
ph = xx[2]
xxSph[0] = r
xxSph[1] = th
xxSph[2] = ph
# Now define xCart, yCart, and zCart in terms of x0,xx[1],xx[2].
# Note that the relation between r and x0 is not necessarily trivial in SinhSpherical coordinates. See above.
xxCart[0] = xxSph[0]*sp.sin(xxSph[1])*sp.cos(xxSph[2])
xxCart[1] = xxSph[0]*sp.sin(xxSph[1])*sp.sin(xxSph[2])
xxCart[2] = xxSph[0]*sp.cos(xxSph[1])
scalefactor_orthog[0] = sp.diff(xxSph[0],xx[0])
scalefactor_orthog[1] = xxSph[0]
scalefactor_orthog[2] = xxSph[0]*sp.sin(xxSph[1])
# Set the unit vectors
UnitVectors = [[ sp.sin(xxSph[1])*sp.cos(xxSph[2]), sp.sin(xxSph[1])*sp.sin(xxSph[2]), sp.cos(xxSph[1])],
[ sp.cos(xxSph[1])*sp.cos(xxSph[2]), sp.cos(xxSph[1])*sp.sin(xxSph[2]), -sp.sin(xxSph[1])],
[ -sp.sin(xxSph[2]), sp.cos(xxSph[2]), sp.sympify(0) ]]
##########################################################################
# CYLINDRICAL-LIKE COORDINATE SYSTEMS WITH & WITHOUT RADIAL/Z RESCALINGS #
##########################################################################
elif CoordSystem == "Cylindrical" or CoordSystem == "SinhCylindrical" or CoordSystem == "SinhCylindricalv2":
# Assuming the cylindrical radial coordinate
# is positive makes nice simplifications of
# unit vectors possible.
xx[0] = sp.symbols("xx0", real=True)
if CoordSystem == "Cylindrical":
RHOMAX,ZMIN,ZMAX = par.Cparameters("REAL",thismodule,["RHOMAX","ZMIN","ZMAX"],[10.0,-10.0,10.0])
xxmin = [sp.sympify(0), -M_PI, ZMIN]
xxmax = [ RHOMAX, M_PI, ZMAX]
RHOCYL = xx[0]
PHICYL = xx[1]
ZCYL = xx[2]
Cart_to_xx[0] = sp.sqrt(Cartx ** 2 + Carty ** 2)
Cart_to_xx[1] = sp.atan2(Carty, Cartx)
Cart_to_xx[2] = Cartz
elif CoordSystem == "SinhCylindrical":
xxmin = [sp.sympify(0), -M_PI, sp.sympify(-1)]
xxmax = [sp.sympify(1), M_PI, sp.sympify(+1)]
AMPLRHO, SINHWRHO, AMPLZ, SINHWZ = par.Cparameters("REAL",thismodule,
["AMPLRHO","SINHWRHO","AMPLZ","SINHWZ"],
[ 10.0, 0.2, 10.0, 0.2])
# Set SinhCylindrical radial & z coordinates by default; overwrite later if CoordSystem == "SinhCylindricalv2".
RHOCYL = AMPLRHO * (sp.exp(xx[0] / SINHWRHO) - sp.exp(-xx[0] / SINHWRHO)) / (sp.exp(1 / SINHWRHO) - sp.exp(-1 / SINHWRHO))
# phi coordinate remains unchanged.
PHICYL = xx[1]
ZCYL = AMPLZ * (sp.exp(xx[2] / SINHWZ) - sp.exp(-xx[2] / SINHWZ)) / (sp.exp(1 / SINHWZ) - sp.exp(-1 / SINHWZ))
Cart_to_xx[0] = SINHWRHO*sp.asinh(sp.sqrt(Cartx ** 2 + Carty ** 2)*sp.sinh(1/SINHWRHO)/AMPLRHO)
Cart_to_xx[1] = sp.atan2(Carty, Cartx)
Cart_to_xx[2] = SINHWZ*sp.asinh(Cartz*sp.sinh(1/SINHWZ)/AMPLZ)
# SinhCylindricalv2 adds the parameters "const_drho", "const_dz", which allows for regions near xx[0]=0
# and xx[2]=0 to have constant rho and z resolution of const_drho and const_dz, provided the sinh() terms
# do not dominate near xx[0]=0 and xx[2]=0.
elif CoordSystem == "SinhCylindricalv2":
xxmin = [sp.sympify(0), -M_PI, sp.sympify(-1)]
xxmax = [sp.sympify(1), M_PI, sp.sympify(+1)]
AMPLRHO, SINHWRHO, AMPLZ, SINHWZ = par.Cparameters("REAL",thismodule,
["AMPLRHO","SINHWRHO","AMPLZ","SINHWZ"],
[ 10.0, 0.2, 10.0, 0.2])
const_drho, const_dz = par.Cparameters("REAL",thismodule,["const_drho","const_dz"],[0.0625,0.0625])
RHOCYL = AMPLRHO * ( const_drho*xx[0] + (sp.exp(xx[0] / SINHWRHO) - sp.exp(-xx[0] / SINHWRHO)) / (sp.exp(1 / SINHWRHO) - sp.exp(-1 / SINHWRHO)) )
PHICYL = xx[1]
ZCYL = AMPLZ * ( const_dz *xx[2] + (sp.exp(xx[2] / SINHWZ ) - sp.exp(-xx[2] / SINHWZ )) / (sp.exp(1 / SINHWZ ) - sp.exp(-1 / SINHWZ )) )
# NO CLOSED-FORM EXPRESSION FOR RADIAL OR Z INVERSION.
# Cart_to_xx[0] = "NewtonRaphson"
# Cart_to_xx[1] = sp.atan2(Carty, Cartx)
# Cart_to_xx[2] = "NewtonRaphson"
xxCart[0] = RHOCYL*sp.cos(PHICYL)
xxCart[1] = RHOCYL*sp.sin(PHICYL)
xxCart[2] = ZCYL
xxSph[0] = sp.sqrt(RHOCYL**2 + ZCYL**2)
xxSph[1] = sp.acos(ZCYL / xxSph[0])
xxSph[2] = PHICYL
scalefactor_orthog[0] = sp.diff(RHOCYL,xx[0])
scalefactor_orthog[1] = RHOCYL
scalefactor_orthog[2] = sp.diff(ZCYL,xx[2])
f0_of_xx0 = RHOCYL
f4_of_xx2 = sp.diff(ZCYL,xx[2])
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0])
scalefactor_orthog_funcform[1] = f0_of_xx0_funcform
scalefactor_orthog_funcform[2] = f4_of_xx2_funcform
# Set the unit vectors
UnitVectors = [[ sp.cos(PHICYL), sp.sin(PHICYL), sp.sympify(0)],
[-sp.sin(PHICYL), sp.cos(PHICYL), sp.sympify(0)],
[ sp.sympify(0), sp.sympify(0), sp.sympify(1)]]
elif CoordSystem == "SymTP" or CoordSystem == "SinhSymTP":
# var1, var2= sp.symbols('var1 var2',real=True)
bScale, SINHWAA, AMAX = par.Cparameters("REAL",thismodule,
["bScale","SINHWAA","AMAX"],
[0.5, 0.2, 10.0 ])
# Assuming xx0, xx1, and bScale
# are positive makes nice simplifications of
# unit vectors possible.
xx[0],xx[1] = sp.symbols("xx0 xx1", real=True)
xxmin = [sp.sympify(0), sp.sympify(0),-M_PI]
xxmax = [ AMAX, M_PI, M_PI]
AA = xx[0]
if CoordSystem == "SinhSymTP":
# With xxmax[0] == AMAX, sinh(xx0/AMAX) will evaluate to a number between 0 and 1.
# Similarly, sinh(xx0/(AMAX*SINHWAA)) / sinh(1/SINHWAA) will also evaluate to a number between 0 and 1.
# Then AA = AMAX*sinh(xx0/(AMAX*SINHWAA)) / sinh(1/SINHWAA) will evaluate to a number between 0 and AMAX.
AA = AMAX * (sp.exp(xx[0] / (AMAX*SINHWAA)) - sp.exp(-xx[0] / (AMAX*SINHWAA))) / (sp.exp(1 / SINHWAA) - sp.exp(-1 / AMAX))
var1 = sp.sqrt(AA**2 + (bScale * sp.sin(xx[1]))**2)
var2 = sp.sqrt(AA**2 + bScale**2)
RHOSYMTP = AA*sp.sin(xx[1])
PHSYMTP = xx[2]
ZSYMTP = var2*sp.cos(xx[1])
xxCart[0] = AA *sp.sin(xx[1])*sp.cos(xx[2])
xxCart[1] = AA *sp.sin(xx[1])*sp.sin(xx[2])
xxCart[2] = ZSYMTP
xxSph[0] = sp.sqrt(RHOSYMTP**2 + ZSYMTP**2)
xxSph[1] = sp.acos(ZSYMTP / xxSph[0])
xxSph[2] = PHSYMTP
if CoordSystem == "SymTP":
rSph = sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)
thSph = sp.acos(Cartz / rSph)
phSph = sp.atan2(Carty, Cartx)
# Mathematica script to compute Cart_to_xx[]
# AA = x1;
# var2 = Sqrt[AA^2 + bScale^2];
# RHOSYMTP = AA*Sin[x2];
# ZSYMTP = var2*Cos[x2];
# Solve[{rSph == Sqrt[RHOSYMTP^2 + ZSYMTP^2],
# thSph == ArcCos[ZSYMTP/Sqrt[RHOSYMTP^2 + ZSYMTP^2]],
# phSph == x3},
# {x1, x2, x3}]
Cart_to_xx[0] = sp.sqrt(-bScale**2 + rSph**2 +
sp.sqrt(bScale**4 + 2*bScale**2*rSph**2 + rSph**4 -
4*bScale**2*rSph**2*sp.cos(thSph)**2))*M_SQRT1_2 # M_SQRT1_2 = 1/sqrt(2); define this way for UnitTesting
# The sign() function in the following expression ensures the correct root is taken.
Cart_to_xx[1] = sp.acos(sp.sign(Cartz)*(
sp.sqrt(1 + rSph**2/bScale**2 -
sp.sqrt(bScale**4 + 2*bScale**2*rSph**2 + rSph**4 -
4*bScale**2*rSph**2*sp.cos(thSph)**2)/bScale**2)*M_SQRT1_2)) # M_SQRT1_2 = 1/sqrt(2); define this way for UnitTesting
Cart_to_xx[2] = phSph
elif CoordSystem == "SinhSymTP":
pass
# Closed form expression for Cart_to_xx in SinhSymTP may exist, but has not yet been found
scalefactor_orthog[0] = sp.diff(AA,xx[0]) * var1 / var2
scalefactor_orthog[1] = var1
scalefactor_orthog[2] = AA * sp.sin(xx[1])
f0_of_xx0 = AA
f1_of_xx1 = sp.sin(xx[1])
f2_of_xx0_xx1 = var1
f3_of_xx0 = var2
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0]) * f2_of_xx0_xx1_funcform/f3_of_xx0_funcform
scalefactor_orthog_funcform[1] = f2_of_xx0_xx1_funcform
scalefactor_orthog_funcform[2] = f0_of_xx0_funcform*f1_of_xx1_funcform
# Set the transpose of the matrix of unit vectors
UnitVectors = [[sp.sin(xx[1]) * sp.cos(xx[2]) * var2 / var1,
sp.sin(xx[1]) * sp.sin(xx[2]) * var2 / var1,
AA * sp.cos(xx[1]) / var1],
[AA * sp.cos(xx[1]) * sp.cos(xx[2]) / var1,
AA * sp.cos(xx[1]) * sp.sin(xx[2]) / var1,
-sp.sin(xx[1]) * var2 / var1],
[-sp.sin(xx[2]), sp.cos(xx[2]), sp.sympify(0)]]
elif CoordSystem == "Cartesian":
xmin, xmax, ymin, ymax, zmin, zmax = par.Cparameters("REAL",thismodule,
["xmin","xmax","ymin","ymax","zmin","zmax"],
[ -10.0, 10.0, -10.0, 10.0, -10.0, 10.0])
xxmin = ["xmin", "ymin", "zmin"]
xxmax = ["xmax", "ymax", "zmax"]
xxCart[0] = xx[0]
xxCart[1] = xx[1]
xxCart[2] = xx[2]
xxSph[0] = sp.sqrt(xx[0] ** 2 + xx[1] ** 2 + xx[2] ** 2)
xxSph[1] = sp.acos(xx[2] / xxSph[0])
xxSph[2] = sp.atan2(xx[1], xx[0])
Cart_to_xx[0] = Cartx
Cart_to_xx[1] = Carty
Cart_to_xx[2] = Cartz
scalefactor_orthog[0] = sp.sympify(1)
scalefactor_orthog[1] = sp.sympify(1)
scalefactor_orthog[2] = sp.sympify(1)
scalefactor_orthog_funcform[0] = sp.sympify(1)
scalefactor_orthog_funcform[1] = sp.sympify(1)
scalefactor_orthog_funcform[2] = sp.sympify(1)
# Set the transpose of the matrix of unit vectors
UnitVectors = [[sp.sympify(1), sp.sympify(0), sp.sympify(0)],
[sp.sympify(0), sp.sympify(1), sp.sympify(0)],
[sp.sympify(0), sp.sympify(0), sp.sympify(1)]]
else:
print("CoordSystem == " + CoordSystem + " is not supported.")
sys.exit(1)
# Finally, call ref_metric__hatted_quantities()
# to construct hatted metric, derivs of hatted
# metric, and Christoffel symbols
ref_metric__hatted_quantities(SymPySimplifyExpressions)
# ref_metric__hatted_quantities(scalefactor_orthog_funcform,SymPySimplifyExpressions)
# ref_metric__hatted_quantities(scalefactor_orthog,SymPySimplifyExpressions)
def ref_metric__hatted_quantities(SymPySimplifyExpressions=True):
enable_rfm_precompute = False
if par.parval_from_str(thismodule+"::enable_rfm_precompute") == "True":
enable_rfm_precompute = True
# Step 0: Set dimension DIM
DIM = par.parval_from_str("grid::DIM")
global ReU,ReDD,ghatDD,ghatUU,detgammahat
ReU = ixp.zerorank1()
ReDD = ixp.zerorank2()
ghatDD = ixp.zerorank2()
# Step 1: Compute ghatDD (reference metric), ghatUU
# (inverse reference metric), as well as
# rescaling vector ReU & rescaling matrix ReDD
if enable_rfm_precompute == False:
for i in range(DIM):
scalefactor_orthog[i] = sp.sympify(scalefactor_orthog[i])
ghatDD[i][i] = scalefactor_orthog[i]**2
ReU[i] = 1/scalefactor_orthog[i]
for j in range(DIM):
ReDD[i][j] = scalefactor_orthog[i]*scalefactor_orthog[j]
else:
for i in range(DIM):
scalefactor_orthog_funcform[i] = sp.sympify(scalefactor_orthog_funcform[i])
ghatDD[i][i] = scalefactor_orthog_funcform[i]**2
ReU[i] = 1/scalefactor_orthog_funcform[i]
for j in range(DIM):
ReDD[i][j] = scalefactor_orthog_funcform[i]*scalefactor_orthog_funcform[j]
# Step 1b: Compute ghatUU
ghatUU, detgammahat = ixp.symm_matrix_inverter3x3(ghatDD)
# Step 1c: Sanity check: verify that ReDD, ghatDD,
# and ghatUU are all symmetric rank-2:
for i in range(DIM):
for j in range(DIM):
if ReDD[i][j] != ReDD[j][i]:
print("Error: ReDD["+ str(i) + "][" + str(j) + "] != ReDD["+ str(j) + "][" + str(i) + ": " + str(ReDD[i][j]) + "!=" + str(ReDD[j][i]))
sys.exit(1)
if ghatDD[i][j] != ghatDD[j][i]:
print("Error: ghatDD["+ str(i) + "][" + str(j) + "] != ghatDD["+ str(j) + "][" + str(i) + ": " + str(ghatDD[i][j]) + "!=" + str(ghatDD[j][i]))
sys.exit(1)
if ghatUU[i][j] != ghatUU[j][i]:
print("Error: ghatUU["+ str(i) + "][" + str(j) + "] != ghatUU["+ str(j) + "][" + str(i) + ": " + str(ghatUU[i][j]) + "!=" + str(ghatUU[j][i]))
sys.exit(1)
# Step 2: Compute det(ghat) and its 1st & 2nd derivatives
global detgammahatdD,detgammahatdDD
detgammahatdD = ixp.zerorank1(DIM)
detgammahatdDD = ixp.zerorank2(DIM)
for i in range(DIM):
detgammahatdD[i] = (sp.diff(detgammahat, xx[i]))
for j in range(DIM):
detgammahatdDD[i][j] = sp.diff(detgammahatdD[i], xx[j])
# Step 3a: Compute 1st & 2nd derivatives of rescaling vector.
# (E.g., needed in BSSN for betaUdDD computation)
global ReUdD,ReUdDD
ReUdD = ixp.zerorank2(DIM)
ReUdDD = ixp.zerorank3(DIM)
for i in range(DIM):
for j in range(DIM):
ReUdD[i][j] = sp.diff(ReU[i], xx[j])
for k in range(DIM):
ReUdDD[i][j][k] = sp.diff(ReUdD[i][j], xx[k])
# Step 3b: Compute 1st & 2nd derivatives of rescaling matrix.
global ReDDdD,ReDDdDD
ReDDdD = ixp.zerorank3(DIM)
ReDDdDD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
ReDDdD[i][j][k] = (sp.diff(ReDD[i][j],xx[k]))
for l in range(DIM):
# Simplifying this doesn't appear to help overall NRPy run time.
ReDDdDD[i][j][k][l] = sp.diff(ReDDdD[i][j][k],xx[l])
# Step 3c: Compute 1st & 2nd derivatives of reference metric.
global ghatDDdD,ghatDDdDD
ghatDDdD = ixp.zerorank3(DIM)
ghatDDdDD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
if SymPySimplifyExpressions==True:
# ghatDDdD[i][j][k] = sp.trigsimp(sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
ghatDDdD[i][j][k] = sp.simplify(sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
else:
ghatDDdD[i][j][k] = (sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
for l in range(DIM):
ghatDDdDD[i][j][k][l] = (sp.diff(ghatDDdD[i][j][k],xx[l]))
# Step 4a: Compute Christoffel symbols of reference metric.
global GammahatUDD
GammahatUDD = ixp.zerorank3(DIM)
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
# GammahatUDD[i][k][l] += sp.trigsimp((sp.Rational(1,2))*ghatUU[i][m]*\
GammahatUDD[i][k][l] += (sp.Rational(1,2))*ghatUU[i][m]*\
(ghatDDdD[m][k][l] + ghatDDdD[m][l][k] - ghatDDdD[k][l][m])
# Step 4b: Compute derivs of Christoffel symbols of reference metric.
global GammahatUDDdD
GammahatUDDdD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammahatUDDdD[i][j][k][l] = (sp.diff(GammahatUDD[i][j][k],xx[l]))
# Step 4c: If rfm_precompute is disabled, then we are finished with this function.
# Otherwise continue to Step 5.
if enable_rfm_precompute == False:
return
else:
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
# if not (("Spherical" in CoordSystem) or ("SymTP" in CoordSystem)):
# if not (( "Spherical" in CoordSystem)):
# print("Error: CoordSystem == "+CoordSystem+" does not yet support rfm precompute infrastructure.")
# sys.exit(1)
# enable_rfm_precompute: precompute and store in memory complicated
# expressions related to the reference metric (a.k.a., "hatted
# quantities")
# The precomputed "hatted quantity" expressions will be stored in
# a C struct called rfmstruct. As these expressions generally
# involve computationally expensive transcendental functions
# of xx0,xx1,or xx2, and xx0,xx1, and xx2 remain fixed across
# most (if not all) of a given simulation, setting up the
# rfmstruct can greatly improve performance.
# The core challenge in setting up the rfmstruct is collecting
# all the information needed to automatically generate it.
# Step 5 and onwards implements this algorithm, using the
# *generic functional form* of the hatted quantities (as
# opposed to the exact closed-form expressions of the
# hatted quantities) computed above.
# Step 5: Now that all hatted quantities are written in terms of generic SymPy functions,
# we will now replace SymPy functions with simple variables using rigid NRPy+ syntax,
# and store these variables to globals defined above.
def make_replacements(expr):
sympy_version = sp.__version__.replace("rc","...").replace("b","...") # Ignore the rc's and b's for release candidates & betas.
sympy_major_version = int(sympy_version.split(".")[0])
sympy_minor_version = int(sympy_version.split(".")[1])
if sympy_major_version < 1 or (sympy_major_version >= 1 and sympy_minor_version < 2):
print("Detected SymPy version "+sympy_version)
print("Sorry, reference metric precomputation unsupported in SymPy < 1.2!")
sys.exit(1)
for item in sp.preorder_traversal(expr):
if item.func == sp.Derivative:
stringfunc = str(item.args[0]).split("_funcform(", 1)[0] # store everything before _funcform(...
stringderv = str(item.args[1]).replace(" ", "") # Ignore whitespace
deriv_wrt = stringderv.split(",")[0].replace("(xx", "")
derivorder = int(stringderv.split(",")[1].replace(")", ""))
derivop = "__D"
for i in range(derivorder - 1):
derivop += "D"
derivop += deriv_wrt
for i in range(derivorder - 1):
derivop += deriv_wrt
expr = expr.xreplace(
{item: sp.sympify(stringfunc + derivop)})
for item in sp.preorder_traversal(expr):
if "_funcform" in str(item.func):
stringfunc = str(item.func).split("_funcform", 1)[0] # store everything before _funcform(...
expr = expr.xreplace({item: sp.sympify(stringfunc)})
return expr
detgammahat = make_replacements(detgammahat)
for i in range(DIM):
ReU[i] = make_replacements(ReU[i])
detgammahatdD[i] = make_replacements(detgammahatdD[i])
for j in range(DIM):
ReDD[i][j] = make_replacements(ReDD[i][j])
ReUdD[i][j] = make_replacements(ReUdD[i][j])
ghatDD[i][j] = make_replacements(ghatDD[i][j])
ghatUU[i][j] = make_replacements(ghatUU[i][j])
detgammahatdDD[i][j] = make_replacements(detgammahatdDD[i][j])
for k in range(DIM):
ReDDdD[i][j][k] = make_replacements(ReDDdD[i][j][k])
ReUdDD[i][j][k] = make_replacements(ReUdDD[i][j][k])
ghatDDdD[i][j][k] = make_replacements(ghatDDdD[i][j][k])
GammahatUDD[i][j][k] = make_replacements(GammahatUDD[i][j][k])
for l in range(DIM):
ReDDdDD[i][j][k][l] = make_replacements(ReDDdDD[i][j][k][l])
ghatDDdDD[i][j][k][l] = make_replacements(ghatDDdDD[i][j][k][l])
GammahatUDDdD[i][j][k][l] = make_replacements(GammahatUDDdD[i][j][k][l])
# Step 6: At this point, each expression is written in terms of the generic functions
# of xx0, xx1, and/or xx2 and their derivatives. Depending on the functions, some
# of these derivatives may be zero. In Step 5 we'll evaluate the function
# derivatives exactly and set the expressions to zero. Otherwise in the C code
# we'd be storing performing arithmetic with zeros -- wasteful!
# Step 6.a: Construct the full list of *unique* NRPy+ variables representing the
# SymPy functions and derivatives, so that all zero derivatives can be
# computed.
freevars = []
freevars.extend(detgammahat.free_symbols)
for i in range(DIM):
freevars.extend(ReU[i].free_symbols)
freevars.extend(detgammahatdD[i].free_symbols)
for j in range(DIM):
freevars.extend(ReDD[i][j].free_symbols)
freevars.extend(ReUdD[i][j].free_symbols)
freevars.extend(ghatDD[i][j].free_symbols)
freevars.extend(ghatUU[i][j].free_symbols)
freevars.extend(detgammahatdDD[i][j].free_symbols)
for k in range(DIM):
freevars.extend(ReDDdD[i][j][k].free_symbols)
freevars.extend(ReUdDD[i][j][k].free_symbols)
freevars.extend(ghatDDdD[i][j][k].free_symbols)
freevars.extend(GammahatUDD[i][j][k].free_symbols)
for l in range(DIM):
freevars.extend(ReDDdDD[i][j][k][l].free_symbols)
freevars.extend(ghatDDdDD[i][j][k][l].free_symbols)
freevars.extend(GammahatUDDdD[i][j][k][l].free_symbols)
freevars_uniq = superfast_uniq(freevars)
freevars_uniq_xx_indep = []
for i in range(len(freevars_uniq)):
freevars_uniq_xx_indep.append(freevars_uniq[i])
# Step 6.b: Using the expressions f?_of_xx? set in reference_metric(),
# evaluate each needed derivative and, in the case it is zero,
# set the corresponding "freevar" variable to zero.
freevars_uniq_vals = []
for i in range(len(freevars_uniq)):
var = freevars_uniq[i]
basename = str(var).split("__")[0].replace("_funcform", "")
derivatv = ""
if "__" in str(var):
derivatv = str(var).split("__")[1].replace("_funcform", "")
if basename == "f0_of_xx0":
basefunc = f0_of_xx0
elif basename == "f1_of_xx1":
basefunc = f1_of_xx1
elif basename == "f2_of_xx0_xx1":
basefunc = f2_of_xx0_xx1
elif basename == "f3_of_xx0":
basefunc = f3_of_xx0
elif basename == "f4_of_xx2":
basefunc = f4_of_xx2
else:
print("Error: function inside " + str(var) + " undefined.")
sys.exit(1)
diff_result = basefunc
if derivatv == "":
pass
else:
derivorder = derivatv.replace("d", "").replace("D", "").replace("0", "0 ").replace("1", "1 ").replace(
"2", "2 ").split(" ")
for derivdirn in derivorder:
if derivdirn != "":
derivwrt = xx[int(derivdirn)]
diff_result = sp.diff(diff_result, derivwrt)
freevars_uniq_vals.append(diff_result)
frees_uniq = superfast_uniq(diff_result.free_symbols)
xx_dep = False
for dirn in range(3):
if gri.xx[dirn] in frees_uniq:
xx_dep = True
if xx_dep == False:
freevars_uniq_xx_indep[i] = diff_result
# Step 6.c: Finally, substitute integers for all functions & derivatives that evaluate to integers
for varidx in range(len(freevars_uniq)):
detgammahat = detgammahat.subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
for i in range(DIM):
ReU[i] = ReU[i].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
detgammahatdD[i] = detgammahatdD[i].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
for j in range(DIM):
ReDD[i][j] = ReDD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ReUdD[i][j] = ReUdD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatDD[i][j] = ghatDD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatUU[i][j] = ghatUU[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
detgammahatdDD[i][j] = detgammahatdDD[i][j].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
for k in range(DIM):
ReDDdD[i][j][k] = ReDDdD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ReUdDD[i][j][k] = ReUdDD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatDDdD[i][j][k] = ghatDDdD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
GammahatUDD[i][j][k] = GammahatUDD[i][j][k].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
for l in range(DIM):
ReDDdDD[i][j][k][l] = ReDDdDD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
ghatDDdDD[i][j][k][l] = ghatDDdDD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
GammahatUDDdD[i][j][k][l] = GammahatUDDdD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
# Step 7: Construct needed C code for declaring rfmstruct, allocating storage for
# rfmstruct arrays, defining each element in each array, reading the
# rfmstruct data from memory (both with and without SIMD enabled), and
# freeing allocated memory for the rfmstrcut arrays.
# struct_str: String that declares the rfmstruct struct.
struct_str = "typedef struct __rfmstruct__ {\n"
define_str = ""
# rfmstruct stores pointers to (so far) 1D arrays. The malloc_str string allocates space for the arrays.
malloc_str = "rfm_struct rfmstruct;\n"
freemm_str = ""
# readvr_str reads the arrays from memory as needed
readvr_str = ["", "", ""]
readvr_SIMD_outer_str = ["", "", ""]
readvr_SIMD_inner_str = ["", "", ""]
# Sort freevars_uniq_vals and freevars_uniq_xx_indep, according to alphabetized freevars_uniq_xx_indep.
# Without this step, the ordering of elements in rfmstruct would be random, and would change each time
# this function was called.
if len(freevars_uniq_xx_indep) > 0:
freevars_uniq_xx_indep, freevars_uniq_vals = (list(x) for x in zip(*sorted(zip(freevars_uniq_xx_indep, freevars_uniq_vals),key=str)))
# Tease out how many variables each function in freevars_uniq_vals
which_freevar = 0
for expr in freevars_uniq_vals:
if "_of_xx" in str(freevars_uniq_xx_indep[which_freevar]):
frees = expr.free_symbols
frees_uniq = superfast_uniq(frees)
xx_list = []
malloc_size = 1
for i in range(3):
if gri.xx[i] in frees_uniq:
xx_list.append(gri.xx[i])
malloc_size *= gri.Nxx_plus_2NGHOSTS[i]
struct_str += "\tREAL *restrict " + str(freevars_uniq_xx_indep[which_freevar]) + ";\n"
malloc_str += "rfmstruct." + str(
freevars_uniq_xx_indep[which_freevar]) + " = (REAL *)malloc(sizeof(REAL)*" + str(malloc_size) + ");\n"
freemm_str += "free(rfmstruct." + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
output_define_and_readvr = False
for dirn in range(3):
if (gri.xx[dirn] in frees_uniq) and not (gri.xx[(dirn+1)%3] in frees_uniq) and not (gri.xx[(dirn+2)%3] in frees_uniq):
define_str += "for(int i"+str(dirn)+"=0;i"+str(dirn)+"<Nxx_plus_2NGHOSTS"+str(dirn)+";i"+str(dirn)+"++) {\n"
define_str += " const REAL xx"+str(dirn)+" = xx["+str(dirn)+"][i"+str(dirn)+"];\n"
define_str += " rfmstruct." + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"] = " + str(sp.ccode(freevars_uniq_vals[which_freevar])) + ";\n"
define_str += "}\n\n"
readvr_str[dirn] += "const REAL " + str(freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + \
str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"];\n"
readvr_SIMD_outer_str[dirn] += "const double NOSIMD" + str(
freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"]; "
readvr_SIMD_outer_str[dirn] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ConstSIMD(NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
readvr_SIMD_inner_str[dirn] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ReadSIMD(&rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"]);\n"
output_define_and_readvr = True
if (output_define_and_readvr == False) and (gri.xx[0] in frees_uniq) and (gri.xx[1] in frees_uniq):
define_str += """
for(int i1=0;i1<Nxx_plus_2NGHOSTS1;i1++) for(int i0=0;i0<Nxx_plus_2NGHOSTS0;i0++) {
const REAL xx0 = xx[0][i0];
const REAL xx1 = xx[1][i1];
rfmstruct.""" + str(freevars_uniq_xx_indep[which_freevar]) + """[i0 + Nxx_plus_2NGHOSTS0*i1] = """ + str(sp.ccode(freevars_uniq_vals[which_freevar])) + """;
}\n\n"""
readvr_str[0] += "const REAL " + str(freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + \
str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1];\n"
readvr_SIMD_outer_str[0] += "const double NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + \
" = rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1]; "
readvr_SIMD_outer_str[0] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ConstSIMD(NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
readvr_SIMD_inner_str[0] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ReadSIMD(&rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1]);\n"
output_define_and_readvr = True
if output_define_and_readvr == False:
print("ERROR: Could not figure out the (xx0,xx1,xx2) dependency within the expression for "+str(freevars_uniq_xx_indep[which_freevar])+":")
print(str(freevars_uniq_vals[which_freevar]))
sys.exit(1)
which_freevar += 1
struct_str += "} rfm_struct;\n\n"
# Step 8: Output needed C code to files
outdir = par.parval_from_str(thismodule+"::rfm_precompute_Ccode_outdir")
with open(outdir + "/rfm_struct__declare.h", "w") as file:
file.write(struct_str)
with open(outdir + "/rfm_struct__malloc.h", "w") as file:
file.write(malloc_str)
with open(outdir + "/rfm_struct__define.h", "w") as file:
file.write(define_str)
for i in range(3):
with open(outdir + "/rfm_struct__read" + str(i) + ".h", "w") as file:
file.write(readvr_str[i])
with open(outdir + "/rfm_struct__SIMD_outer_read" + str(i) + ".h", "w") as file:
file.write(readvr_SIMD_outer_str[i])
with open(outdir + "/rfm_struct__SIMD_inner_read" + str(i) + ".h", "w") as file:
file.write(readvr_SIMD_inner_str[i])
with open(outdir + "/rfm_struct__freemem.h", "w") as file:
file.write(freemm_str)
def get_EigenCoord():
CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
for EigenCoordstr in ["Spherical","Cylindrical","SymTP","Cartesian"]:
if EigenCoordstr in CoordSystem_orig:
return EigenCoordstr
print("Error: Could not find EigenCoord for reference_metric::CoordSystem == "+CoordSystem_orig)
sys.exit(1)
def set_Nxx_dxx_invdx_params__and__xx_h(outdir="."):
import os
with open(os.path.join(outdir,"set_Nxx_dxx_invdx_params__and__xx.h"),"w") as file:
file.write("""
void set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3],
paramstruct *restrict params, REAL *restrict xx[3]) {
// Override parameter defaults with values based on command line arguments and NGHOSTS.
params->Nxx0 = Nxx[0];
params->Nxx1 = Nxx[1];
params->Nxx2 = Nxx[2];
params->Nxx_plus_2NGHOSTS0 = Nxx[0] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS1 = Nxx[1] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS2 = Nxx[2] + 2*NGHOSTS;
// Step 0d: Set up space and time coordinates
// Step 0d.i: Declare \Delta x^i=dxx{0,1,2} and invdxx{0,1,2}, as well as xxmin[3] and xxmax[3]:
#include "set_Cparameters.h"
REAL xxmin[3],xxmax[3];
if(EigenCoord == 0) {
""")
for i in range(3):
file.write(" xxmin["+str(i)+"] = "+str(xxmin[i])+";\n")
file.write(" xxmax["+str(i)+"] = "+str(xxmax[i])+";\n")
file.write("""
} else if (EigenCoord == 1) {
""")
CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
par.set_parval_from_str("reference_metric::CoordSystem",get_EigenCoord())
reference_metric()
for i in range(3):
file.write(" xxmin["+str(i)+"] = "+str(xxmin[i])+";\n")
file.write(" xxmax["+str(i)+"] = "+str(xxmax[i])+";\n")
par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem_orig)
reference_metric()
file.write("""
}
params->dxx0 = (xxmax[0] - xxmin[0]) / ((REAL)Nxx[0]);
params->dxx1 = (xxmax[1] - xxmin[1]) / ((REAL)Nxx[1]);
params->dxx2 = (xxmax[2] - xxmin[2]) / ((REAL)Nxx[2]);
params->invdx0 = 1.0/params->dxx0;
params->invdx1 = 1.0/params->dxx1;
params->invdx2 = 1.0/params->dxx2;
// Now that params.dxx{0,1,2} and params.invdxx{0,1,2} have been set,
// Step 0d.iii: Set up uniform coordinate grids
xx[0] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS0);
for(int j=0;j<Nxx_plus_2NGHOSTS0;j++)
xx[0][j] = xxmin[0] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx0; // Cell-centered grid.
xx[1] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS1);
for(int j=0;j<Nxx_plus_2NGHOSTS1;j++)
xx[1][j] = xxmin[1] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx1; // Cell-centered grid.
xx[2] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS2);
for(int j=0;j<Nxx_plus_2NGHOSTS2;j++)
xx[2][j] = xxmin[2] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx2; // Cell-centered grid.
//fprintf(stderr,"hey inside setxx: %e %e %e | %e %e\\n",xxmin[0],xxmin[1],xxmin[2],xx[0][0],params->dxx0);
}
""")
def xxCart_h(funcname,cparamsloc,outfile):
import outputC
# Arbitrary-coordinate NRPy+ file output, Part 1: output the conversion from (x0,x1,x2) to Cartesian (x,y,z)
Cout = outputC.outputC([xxCart[0],xxCart[1],xxCart[2]],
["xCart[0]","xCart[1]","xCart[2]"],
"returnstring",params="preindent=1")
with open(outfile, "w") as file:
file.write("""
inline void """+funcname+"""(const paramstruct *restrict params, REAL *restrict xx[3],const int i0,const int i1,const int i2, REAL xCart[3]) {
#include """+"\""+cparamsloc+"\""+"""
REAL xx0 = xx[0][i0];
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];\n"""+Cout+"}\n")
# Compute proper distance in all 3 directions. Used to find the appropriate timestep for the CFL condition.
def ds_dirn(delxx):
ds_dirn = ixp.zerorank1(3)
for i in range(3):
ds_dirn[i] = delxx[i]*scalefactor_orthog[i]
return ds_dirn
# Find the appropriate timestep for the CFL condition.
def add_find_timestep_func_to_dict():
# Compute proper distance in all 3 directions.
delxx = ixp.declarerank1("dxx", DIM=3)
ds_drn = ds_dirn(delxx)
ds_dirn_h = outputC([ds_drn[0], ds_drn[1], ds_drn[2]], ["ds_dirn0", "ds_dirn1", "ds_dirn2"],"returnstring")
desc="Find the CFL-constrained timestep"
add_to_Cfunction_dict(
desc =desc,
type ="REAL",
name ="find_timestep",
params ="const paramstruct *restrict params, REAL *restrict xx[3]",
preloop ="REAL dsmin = 1e38; // Start with a crazy high value... close to the largest number in single precision.",
body ="REAL ds_dirn0, ds_dirn1, ds_dirn2;\n"+ds_dirn_h+"""
#ifndef MIN
#define MIN(A, B) ( ((A) < (B)) ? (A) : (B) )
#endif
// Set dsmin = MIN(dsmin, ds_dirn0, ds_dirn1, ds_dirn2);
dsmin = MIN(dsmin,MIN(ds_dirn0,MIN(ds_dirn1,ds_dirn2)));
""",
loopopts ="InteriorPoints,Read_xxs,DisableOpenMP",
postloop ="return dsmin*CFL_FACTOR/wavespeed;\n")
def out_timestep_func_to_file(outfile):
add_find_timestep_func_to_dict()
with open(outfile, "w") as file:
file.write(outC_function_dict["find_timestep"])
def out_default_free_parameters_for_rfm(free_parameters_file,
domain_size=1.0,sinh_width=0.4,sinhv2_const_dr=0.05,SymTP_bScale=0.5):
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
with open(free_parameters_file, "a") as file:
file.write("""
// Set free-parameter values.
const REAL domain_size = """ + str(domain_size) + """;
const REAL sinh_width = """ + str(sinh_width) + """;
const REAL sinhv2_const_dr= """ + str(sinhv2_const_dr) + """;
const REAL SymTP_bScale = """ + str(SymTP_bScale) + ";\n")
coordparams = ""
if CoordSystem == "Spherical":
coordparams += """
params.RMAX = domain_size;\n"""
elif "SinhSpherical" in CoordSystem:
coordparams += """
params.AMPL = domain_size;
params.SINHW= sinh_width;\n"""
if CoordSystem == "SinhSphericalv2":
coordparams += " params.const_dr = sinhv2_const_dr;\n"
elif "SymTP" in CoordSystem:
coordparams += """
params.bScale = SymTP_bScale;
params.AMAX = domain_size;\n"""
if CoordSystem == "SinhSymTP":
coordparams += " params.SINHWAA = sinh_width;\n"
elif CoordSystem == "Cartesian":
coordparams += """
params.xmin = -domain_size, params.xmax = domain_size;
params.ymin = -domain_size, params.ymax = domain_size;
params.zmin = -domain_size, params.zmax = domain_size;\n"""
elif CoordSystem == "Cylindrical":
coordparams += """
params.ZMIN = -domain_size;
params.ZMAX = domain_size;
params.RHOMAX = domain_size;\n"""
elif "SinhCylindrical" in CoordSystem:
coordparams += """
params.AMPLRHO = domain_size;
params.SINHWRHO= sinh_width;
params.AMPLZ = domain_size;
params.SINHWZ = sinh_width;\n"""
if CoordSystem == "SinhCylindricalv2":
coordparams += """
params.const_drho = sinhv2_const_dr;
params.const_dz = sinhv2_const_dr;\n"""
file.write(coordparams + "\n")
IMPLEMENTS: StaticTrumpet
INHERITS: ADMBase
USES INCLUDE HEADER: loop.hxx
ActiveThorns = "
ADMBase
BaikalX
CarpetX
ErrorEstimator
IOUtil
ODESolvers
StaticTrumpet
"
$nlevels = 1 # 2 #TODO 8
$ncells = 128 #TODO 32
Cactus::cctk_show_schedule = yes
Cactus::terminate = "time"
Cactus::cctk_final_time = 1.0
CarpetX::verbose = yes
CarpetX::xmin = -6.4 #TODO -16.0
CarpetX::ymin = -6.4 #TODO -16.0
CarpetX::zmin = -6.4 #TODO -16.0
CarpetX::xmax = +6.4 #TODO +16.0
CarpetX::ymax = +6.4 #TODO +16.0
CarpetX::zmax = +6.4 #TODO +16.0
CarpetX::ncells_x = $ncells
CarpetX::ncells_y = $ncells
CarpetX::ncells_z = $ncells
CarpetX::ghost_size = 2
CarpetX::max_num_levels = $nlevels
CarpetX::regrid_every = 16
CarpetX::regrid_error_threshold = 1.0 #TODO 1.0 / 16.0
ErrorEstimator::region_shape = "cube"
ErrorEstimator::scale_by_resolution = no #TODO yes
CarpetX::prolongation_type = "ddf"
CarpetX::prolongation_order = 3
CarpetX::dtfac = 0.25
ADMBase::initial_data = "Static Trumpet"
ADMBase::initial_lapse = "Static Trumpet"
ADMBase::initial_shift = "Static Trumpet"
ADMBase::initial_dtshift = "Static Trumpet"
IO::out_dir = $parfile
IO::out_every = 1 #TODO $ncells * 2 ** ($nlevels - 1) / 32
CarpetX::out_tsv = no
SHARES: ADMBase
EXTENDS KEYWORD initial_data "Initial metric and extrinsic curvature datasets"
{
"Static Trumpet" :: "Static Trumpet black hole"
}
EXTENDS KEYWORD initial_lapse "Initial lapse value"
{
"Static Trumpet" :: "Static Trumpet black hole"
}
EXTENDS KEYWORD initial_shift "Initial lapse value"
{
"Static Trumpet" :: "Static Trumpet black hole"
}
EXTENDS KEYWORD initial_dtshift "Initial lapse value"
{
"Static Trumpet" :: "Static Trumpet black hole"
}
if (CCTK_EQUALS(initial_data, "Static Trumpet") ||
CCTK_EQUALS(initial_lapse, "Static Trumpet") ||
CCTK_EQUALS(initial_shift, "Static Trumpet") ||
CCTK_EQUALS(initial_dtshift, "Static Trumpet")) {
SCHEDULE StaticTrumpet_Initial IN ADMBase_InitialData
{
LANG: C
WRITES: ADMBase::metric(everywhere)
WRITES: ADMBase::curv(everywhere)
WRITES: ADMBase::lapse(everywhere)
WRITES: ADMBase::shift(everywhere)
WRITES: ADMBase::dtshift(everywhere)
SYNC: ADMBase::metric
SYNC: ADMBase::curv
SYNC: ADMBase::lapse
SYNC: ADMBase::shift
SYNC: ADMBase::dtshift
} "Set up static trumpet initial conditions"
}
#include <loop.hxx>
#include <cctk.h>
#include <cctk_Arguments.h>
#include <cctk_Parameters.h>
#include <cmath>
namespace StaticTrumpet {
using namespace Loop;
using namespace std;
template <typename T> T pow2(const T x) { return x * x; }
template <typename T> T pow4(const T x) {
const auto x2 = pow2(x);
return x2 * x2;
}
extern "C" void StaticTrumpet_Initial(CCTK_ARGUMENTS) {
DECLARE_CCTK_ARGUMENTS;
DECLARE_CCTK_PARAMETERS;
const bool set_metric = CCTK_EQUALS(initial_data, "Static Trumpet");
const bool set_lapse = CCTK_EQUALS(initial_lapse, "Static Trumpet");
const bool set_shift = CCTK_EQUALS(initial_shift, "Static Trumpet");
const bool set_dtshift = CCTK_EQUALS(initial_dtshift, "Static Trumpet");
const GF3D<CCTK_REAL, 0, 0, 0> gxx_(cctkGH, gxx);
const GF3D<CCTK_REAL, 0, 0, 0> gxy_(cctkGH, gxy);
const GF3D<CCTK_REAL, 0, 0, 0> gxz_(cctkGH, gxz);
const GF3D<CCTK_REAL, 0, 0, 0> gyy_(cctkGH, gyy);
const GF3D<CCTK_REAL, 0, 0, 0> gyz_(cctkGH, gyz);
const GF3D<CCTK_REAL, 0, 0, 0> gzz_(cctkGH, gzz);
const GF3D<CCTK_REAL, 0, 0, 0> kxx_(cctkGH, kxx);
const GF3D<CCTK_REAL, 0, 0, 0> kxy_(cctkGH, kxy);
const GF3D<CCTK_REAL, 0, 0, 0> kxz_(cctkGH, kxz);
const GF3D<CCTK_REAL, 0, 0, 0> kyy_(cctkGH, kyy);
const GF3D<CCTK_REAL, 0, 0, 0> kyz_(cctkGH, kyz);
const GF3D<CCTK_REAL, 0, 0, 0> kzz_(cctkGH, kzz);
const GF3D<CCTK_REAL, 0, 0, 0> alp_(cctkGH, alp);
// const GF3D<CCTK_REAL, 0, 0, 0> dtalp_(cctkGH, dtalp);
const GF3D<CCTK_REAL, 0, 0, 0> betax_(cctkGH, betax);
const GF3D<CCTK_REAL, 0, 0, 0> betay_(cctkGH, betay);
const GF3D<CCTK_REAL, 0, 0, 0> betaz_(cctkGH, betaz);
const GF3D<CCTK_REAL, 0, 0, 0> dtbetax_(cctkGH, dtbetax);
const GF3D<CCTK_REAL, 0, 0, 0> dtbetay_(cctkGH, dtbetay);
const GF3D<CCTK_REAL, 0, 0, 0> dtbetaz_(cctkGH, dtbetaz);
constexpr CCTK_REAL rmin = 1.0e-2;
constexpr CCTK_REAL M = 1;
loop_all<0, 0, 0>(cctkGH, [&](const PointDesc &p) {
const auto r = sqrt(pow2(p.x) + pow2(p.y) + pow2(p.z));
const auto r1 = 1 / fmax(rmin, r);
const auto rho = sqrt(pow2(p.x) * pow2(p.y));
const auto rho1 = 1 / fmax(rmin, rho);
// x = r sin theta cos phi
// y = r sin theta sin phi
// z = r cos theta
// rho = r sin theta = sqrt (x^2 + y^2)
// dx/dr = x / r
// dy/dr = y / r
// dz/dr = z / r
// dx/dtheta = r cos theta cos phi = x * z / rho
// dy/dtheta = r cos theta sin phi = y * z / rho
// dz/dtheta = - r sin theta = - rho
// dx/dphi = - r sin theta sin phi = - y
// dy/dphi = r sin theta cos phi = x
// dz/dphi = 0
const auto ex_r = p.x * r1;
const auto ey_r = p.y * r1;
const auto ez_r = p.z * r1;
const auto ex_theta = +p.x * p.z * rho1;
const auto ey_theta = +p.y * p.z * rho1;
const auto ez_theta = -rho;
const auto ex_phi = -p.y;
const auto ey_phi = +p.x;
const auto ez_phi = -rho;
// first index i (cartesian), second index a (spherical)
const array<array<CCTK_REAL, 3>, 3> dX_dR{{
{{ex_r, ex_theta, ex_phi}},
{{ey_r, ey_theta, ey_phi}},
{{ez_r, ez_theta, ez_phi}},
}};
const auto phi = sqrt(1 + M * r1);
const auto phi4 = pow4(phi);
// array<array<CCTK_REAL, 3>, 3>
// gij{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
// gij[0][0] = phi4;
// gij[1][1] = phi4;
// gij[2][2] = phi4;
// array<array<CCTK_REAL, 3>, 3>
// guij{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
// guij[0][0] = 1 / phi4;
// guij[1][1] = 1 / phi4;
// guij[2][2] = 1 / phi4;
// array<array<CCTK_REAL, 3>, 3>
// gab{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
// gab[0][0] = phi4;
// gab[1][1] = pow2(r);
// gab[2][2] = pow2(rho);
// array<array<CCTK_REAL, 3>, 3>
// guab{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
// guab[0][0] = 1 / phi4;
// guab[1][1] = pow2(r1);
// guab[2][2] = pow2(rho1);
if (set_metric) {
gxx_(p.I) = phi4;
gxy_(p.I) = 0;
gxz_(p.I) = 0;
gyy_(p.I) = phi4;
gyz_(p.I) = 0;
gzz_(p.I) = phi4;
}
// Set K_ab in spherical coordinates
// array<array<CCTK_REAL, 3>, 3>
// kab{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
// kab[0][0] = -M * pow2(r1);
// kab[1][1] = M;
// kab[2][2] = M * pow2(rho * r1);
// Raise indices
array<array<CCTK_REAL, 3>, 3> kuab{{{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}}};
kuab[0][0] = -M * pow2(r1) / pow2(phi4);
kuab[1][1] = M * pow2(r1);
kuab[2][2] = M * pow2(r1); // M * pow2(rho * r1) * pow2(rho1)
// Convert to cartesian coordinates
array<array<CCTK_REAL, 3>, 3> kuij;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) {
CCTK_REAL t = 0;
for (int a = 0; a < 3; ++a)
for (int b = 0; b < 3; ++b)
t += dX_dR[i][a] * dX_dR[j][b] * kuab[a][b];
kuij[i][j] = t;
}
// Symmetrize
for (int i = 0; i < 3; ++i)
for (int j = 0; j < i; ++j) {
const auto t = (kuij[i][j] + kuij[j][i]) / 2;
kuij[i][j] = kuij[j][i] = t;
}
// Lower indices
array<array<CCTK_REAL, 3>, 3> kij;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
kij[i][j] = pow2(phi4) * kuij[i][j];
if (set_metric) {
kxx_(p.I) = kij[0][0];
kxy_(p.I) = kij[0][1];
kxz_(p.I) = kij[0][2];
kyy_(p.I) = kij[1][1];
kyz_(p.I) = kij[1][2];
kzz_(p.I) = kij[2][2];
}
if (set_lapse)
alp_(p.I) = r / (M + r1);
// dtalp_(p.I) = 0;
if (set_shift) {
const auto betar = M * r / pow2(M + r1);
betax_(p.I) = betar * ex_r;
betay_(p.I) = betar * ey_r;
betaz_(p.I) = betar * ez_r;
}
if (set_dtshift) {
dtbetax_(p.I) = 0;
dtbetay_(p.I) = 0;
dtbetaz_(p.I) = 0;
}
});
}
} // namespace StaticTrumpet
# Main make.code.defn file for thorn StaticTrumpet
# Source files in this directory
SRCS = StaticTrumpet.cxx