Skip to content

Commit 1311ad7

Browse files
committed
Covariant derivative interface for kerr initial data (extrinsic curvature tensor construction)
1 parent c0d0f31 commit 1311ad7

26 files changed

Lines changed: 2187 additions & 1553 deletions

Tests/CMakeLists.txt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,16 @@ set(TEST_BSSN_EVOLUTION_SOURCES
2727
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/ATildeEvolutionTests.cpp
2828
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/KEvolutionTests.cpp
2929
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/GammaEvolutionTests.cpp
30-
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/GaugeEvolutionTests.cpp
31-
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/RK4TimeEvolutionTests.cpp)
30+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/evolution/GaugeEvolutionTests.cpp)
31+
32+
set(TEST_BSSN_STABILITY_SOURCES
33+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/minkowski_stability.cpp
34+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/schwarzschild_stability.cpp
35+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/kerr_basic.cpp
36+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/bowen_york_boost_stability.cpp
37+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/brown_spherical_analytic_constraints.cpp
38+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/brown_hyperbolicity_speeds.cpp
39+
${CMAKE_CURRENT_SOURCE_DIR}/bssn/boundary_conditions.cpp)
3240

3341
set(BENCHMARK_SOURCES
3442
${CMAKE_CURRENT_SOURCE_DIR}/core/MatrixBenchmark.cpp)
@@ -47,13 +55,15 @@ tensorium_add_object_library(test_bssn_constraints_objs ${TEST_BSSN_CONSTRAINT_S
4755
tensorium_add_object_library(test_bssn_ricci_objs ${TEST_BSSN_RICCI_SOURCES})
4856
tensorium_add_object_library(test_bssn_solvers_objs ${TEST_BSSN_SOLVER_SOURCES})
4957
tensorium_add_object_library(test_bssn_evolution_objs ${TEST_BSSN_EVOLUTION_SOURCES})
58+
tensorium_add_object_library(test_bssn_stability_objs ${TEST_BSSN_STABILITY_SOURCES})
5059

5160
set(BSSN_OBJECTS
5261
$<TARGET_OBJECTS:test_bssn_initial_objs>
5362
$<TARGET_OBJECTS:test_bssn_constraints_objs>
5463
$<TARGET_OBJECTS:test_bssn_ricci_objs>
5564
$<TARGET_OBJECTS:test_bssn_solvers_objs>
56-
$<TARGET_OBJECTS:test_bssn_evolution_objs>)
65+
$<TARGET_OBJECTS:test_bssn_evolution_objs>
66+
$<TARGET_OBJECTS:test_bssn_stability_objs>)
5767

5868
add_executable(TensoriumChiEvolutionDemo
5969
bssn/binaries/BSSNChiEvolutionDemo.cpp)

Tests/bssn/boundary_conditions.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "../framework/Assertions.hpp"
2+
#include "../framework/TestRegistry.hpp"
3+
4+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/Geometry/BSSNInvariants.hpp"
5+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/Grid/BSSNGridOperations.hpp"
6+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/InitialData/BSSNInitialData.hpp"
7+
8+
#include <algorithm>
9+
#include <cmath>
10+
11+
namespace {
12+
13+
using Grid = tensorium_RG::BSSNGridSoA<double>;
14+
15+
void center_grid(Grid &grid) {
16+
const double hx = 0.5 * (static_cast<double>(grid.dims.nx) - 1.0) * grid.dx;
17+
const double hy = 0.5 * (static_cast<double>(grid.dims.ny) - 1.0) * grid.dy;
18+
const double hz = 0.5 * (static_cast<double>(grid.dims.nz) - 1.0) * grid.dz;
19+
grid.x0 = -hx;
20+
grid.y0 = -hy;
21+
grid.z0 = -hz;
22+
}
23+
24+
void seed_chi_shell(Grid &grid, double amplitude, size_t shell) {
25+
size_t i0, i1, j0, j1, k0, k1;
26+
grid.domain_bounds(i0, i1, j0, j1, k0, k1);
27+
if (shell == 0)
28+
return;
29+
30+
for (size_t i = i0; i < i1; ++i) {
31+
const size_t di = std::min(i - i0, i1 - 1 - i);
32+
for (size_t j = j0; j < j1; ++j) {
33+
const size_t dj = std::min(j - j0, j1 - 1 - j);
34+
for (size_t k = k0; k < k1; ++k) {
35+
const size_t dk = std::min(k - k0, k1 - 1 - k);
36+
const size_t dmin = std::min({di, dj, dk});
37+
if (dmin >= shell)
38+
continue;
39+
const double weight = static_cast<double>(shell - dmin) / shell;
40+
const size_t idx = grid.chi.idx(i, j, k);
41+
grid.chi.ptr()[idx] = 1.0 + amplitude * weight;
42+
}
43+
}
44+
}
45+
}
46+
47+
double max_shell_deviation(const Grid &grid, size_t shell) {
48+
size_t i0, i1, j0, j1, k0, k1;
49+
grid.domain_bounds(i0, i1, j0, j1, k0, k1);
50+
double max_dev = 0.0;
51+
if (shell == 0)
52+
return max_dev;
53+
54+
for (size_t i = i0; i < i1; ++i) {
55+
const size_t di = std::min(i - i0, i1 - 1 - i);
56+
for (size_t j = j0; j < j1; ++j) {
57+
const size_t dj = std::min(j - j0, j1 - 1 - j);
58+
for (size_t k = k0; k < k1; ++k) {
59+
const size_t dk = std::min(k - k0, k1 - 1 - k);
60+
const size_t dmin = std::min({di, dj, dk});
61+
if (dmin >= shell)
62+
continue;
63+
const size_t idx = grid.chi.idx(i, j, k);
64+
const double dev = std::abs(static_cast<double>(grid.chi.ptr()[idx]) - 1.0);
65+
max_dev = std::max(max_dev, dev);
66+
}
67+
}
68+
}
69+
return max_dev;
70+
}
71+
72+
void perturb_metric_shell(Grid &grid, double amplitude, size_t shell) {
73+
size_t i0, i1, j0, j1, k0, k1;
74+
grid.domain_bounds(i0, i1, j0, j1, k0, k1);
75+
if (shell == 0)
76+
return;
77+
78+
using tensorium_RG::XX;
79+
using tensorium_RG::YY;
80+
using tensorium_RG::ZZ;
81+
82+
for (size_t i = i0; i < i1; ++i) {
83+
const size_t di = std::min(i - i0, i1 - 1 - i);
84+
for (size_t j = j0; j < j1; ++j) {
85+
const size_t dj = std::min(j - j0, j1 - 1 - j);
86+
for (size_t k = k0; k < k1; ++k) {
87+
const size_t dk = std::min(k - k0, k1 - 1 - k);
88+
const size_t dmin = std::min({di, dj, dk});
89+
if (dmin >= shell)
90+
continue;
91+
const double weight = static_cast<double>(shell - dmin) / shell;
92+
const double pulse = amplitude * weight;
93+
const double gxx = 1.0 + pulse;
94+
const double gyy = 1.0 + pulse;
95+
const double gzz = 1.0 - 2.0 * pulse;
96+
const size_t idx = grid.gamma_tilde[XX].idx(i, j, k);
97+
grid.gamma_tilde[XX].ptr()[idx] = gxx;
98+
grid.gamma_tilde[YY].ptr()[idx] = gyy;
99+
grid.gamma_tilde[ZZ].ptr()[idx] = gzz;
100+
grid.gamma_tilde_inv[XX].ptr()[idx] = 1.0 / gxx;
101+
grid.gamma_tilde_inv[YY].ptr()[idx] = 1.0 / gyy;
102+
grid.gamma_tilde_inv[ZZ].ptr()[idx] = 1.0 / gzz;
103+
}
104+
}
105+
}
106+
}
107+
108+
REGISTER_TEST("bssn.bc.outgoing_wave", "Sponge boundary damps outgoing chi pulses", []() {
109+
const size_t nx = 40;
110+
const size_t ny = 40;
111+
const size_t nz = 40;
112+
const size_t ng = 8;
113+
const double spacing = 0.5;
114+
const size_t shell = 6;
115+
const double amplitude = 0.1;
116+
117+
Grid clamp(nx, ny, nz, ng, spacing, spacing, spacing);
118+
Grid sponge(nx, ny, nz, ng, spacing, spacing, spacing);
119+
120+
auto configure = [&](Grid &grid) {
121+
center_grid(grid);
122+
tensorium_RG::init::minkowski(grid, 0.0);
123+
seed_chi_shell(grid, amplitude, shell);
124+
};
125+
126+
configure(clamp);
127+
configure(sponge);
128+
129+
tensorium_RG::bssn::apply_halos_grid<tensorium_RG::bssn::BoundaryClamp>(clamp);
130+
tensorium_RG::bssn::apply_halos_grid<tensorium_RG::bssn::BoundarySponge>(sponge);
131+
132+
const double clamp_dev = max_shell_deviation(clamp, shell);
133+
const double sponge_dev = max_shell_deviation(sponge, shell);
134+
135+
TENSORIUM_TEST_ASSERT(clamp_dev > 0.5 * amplitude);
136+
TENSORIUM_TEST_ASSERT(sponge_dev < 0.35 * clamp_dev);
137+
});
138+
139+
REGISTER_TEST("bssn.bc.by_boundary_localization",
140+
"Determinant drift localizes near the computational boundary", []() {
141+
const size_t nx = 48;
142+
const size_t ny = 48;
143+
const size_t nz = 48;
144+
const size_t ng = 8;
145+
const double spacing = 0.5;
146+
const size_t shell = 4;
147+
const double amplitude = 0.2;
148+
149+
Grid grid(nx, ny, nz, ng, spacing, spacing, spacing);
150+
center_grid(grid);
151+
tensorium_RG::init::minkowski(grid, 0.0);
152+
perturb_metric_shell(grid, amplitude, shell);
153+
154+
const auto stats = tensorium_RG::bssn::compute_invariant_stats(grid, 1);
155+
TENSORIUM_TEST_ASSERT(stats.max_det_deviation > 0.1 * amplitude);
156+
157+
size_t i0, i1, j0, j1, k0, k1;
158+
grid.domain_bounds(i0, i1, j0, j1, k0, k1);
159+
const auto dist = [&](size_t idx, size_t lo, size_t hi) {
160+
return std::min(idx - lo, hi - 1 - idx);
161+
};
162+
const size_t di = dist(stats.det_location.i, i0, i1);
163+
const size_t dj = dist(stats.det_location.j, j0, j1);
164+
const size_t dk = dist(stats.det_location.k, k0, k1);
165+
const size_t min_dist = std::min({di, dj, dk});
166+
TENSORIUM_TEST_ASSERT(min_dist < shell);
167+
});
168+
169+
} // namespace
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#include "../framework/TestRegistry.hpp"
2+
#include "stability_common.hpp"
3+
#include <array>
4+
#include <cmath>
5+
#include <cstdlib>
6+
#include <fstream>
7+
#include <iomanip>
8+
#include <iostream>
9+
#include <sstream>
10+
11+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/Fields/BSSNGridSoA.hpp"
12+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/InitialData/BSSNInitialData.hpp"
13+
#include "../../includes/Tensorium/Physics/DiffGeometry/BSSN_Grid/TimeIntegration/BSSNRK4.hpp"
14+
15+
namespace {
16+
17+
void export_slice_csv(const tensorium::tests::Grid &grid, size_t step,
18+
const std::string &output_dir) {
19+
std::stringstream ss;
20+
ss << output_dir << "/slice_" << std::setw(4) << std::setfill('0') << step << ".csv";
21+
22+
std::ofstream file(ss.str());
23+
if (!file.is_open())
24+
return;
25+
26+
file << "x,y,alpha,W,mask\n";
27+
28+
const size_t nx = grid.dims.nx;
29+
const size_t ny = grid.dims.ny;
30+
const size_t ng = grid.dims.ng;
31+
const size_t k = ng + grid.dims.nz / 2;
32+
33+
for (size_t i = ng; i < nx + ng; ++i) {
34+
for (size_t j = ng; j < ny + ng; ++j) {
35+
const double x = grid.x0 + (double(i) - double(ng)) * grid.dx;
36+
const double y = grid.y0 + (double(j) - double(ng)) * grid.dy;
37+
38+
const size_t idx = grid.alpha.idx(i, j, k);
39+
40+
const double alpha = grid.alpha.ptr()[idx];
41+
const double chi = grid.chi.ptr()[idx];
42+
const double W = std::sqrt(std::max(chi, 1e-16));
43+
44+
const double mask = (alpha < 0.1) ? 1.0 : 0.0;
45+
46+
file << x << "," << y << "," << alpha << "," << W << "," << mask << "\n";
47+
}
48+
}
49+
}
50+
51+
using tensorium::tests::Grid;
52+
53+
void initialize_single_boost(Grid &grid) {
54+
const double m1 = 1.0;
55+
const double m2 = 1.0;
56+
57+
const double x1 = -3.0, y1 = 0.0, z1 = 0.0;
58+
const double x2 = 3.0, y2 = 0.0, z2 = 0.0;
59+
60+
const double Py = 0.095;
61+
const double P1[3] = {0.0, Py, 0.0};
62+
const double P2[3] = {0.0, -Py, 0.0};
63+
64+
const double S1[3] = {0.0, 0.0, 0.1};
65+
const double S2[3] = {0.0, 0.0, 0.1};
66+
67+
tensorium_RG::init::binary_bowen_york_puncture_init(grid, m1, x1, y1, z1, P1, S1, m2, x2, y2,
68+
z2, P2, S2, 1e-10);
69+
}
70+
71+
} // namespace
72+
73+
REGISTER_TEST(
74+
"bssn.viz.moving_puncture", "Export CSV slices of a moving spinning black hole", []() {
75+
tensorium::tests::StabilityRunConfig cfg;
76+
cfg.nx = 64;
77+
cfg.ny = 64;
78+
cfg.nz = 64;
79+
cfg.spacing = 0.1;
80+
cfg.ng = 4;
81+
cfg.padding = 4;
82+
cfg.steps = 600;
83+
cfg.cfl = 0.25;
84+
cfg.gauge_factor = 1.0;
85+
86+
tensorium_RG::fd::set_fd_dx(cfg.spacing);
87+
(void)system("mkdir -p Output/viz");
88+
89+
tensorium::tests::Grid grid(cfg.nx, cfg.ny, cfg.nz, cfg.ng, cfg.spacing, cfg.spacing,
90+
cfg.spacing);
91+
92+
grid.x0 = -0.5 * cfg.spacing * cfg.nx + 0.5 * cfg.spacing;
93+
grid.y0 = -0.5 * cfg.spacing * cfg.ny + 0.5 * cfg.spacing;
94+
grid.z0 = -0.5 * cfg.spacing * cfg.nz + 0.5 * cfg.spacing;
95+
96+
initialize_single_boost(grid);
97+
98+
tensorium_RG::bssn::ProjectionConfig proj_cfg;
99+
proj_cfg.padding = 0;
100+
proj_cfg.renormalize_metric = true;
101+
proj_cfg.project_A_tilde = true;
102+
proj_cfg.recompute_inverse = true;
103+
104+
tensorium_RG::bssn::GaugeParameters<double> params;
105+
params.eta = 3.0;
106+
params.beta_B_coeff = 0.5;
107+
108+
tensorium_RG::bssn::project_bssn_state(grid, proj_cfg);
109+
110+
tensorium_RG::bssn::BSSNRKStepper<double, tensorium_RG::bssn::BoundaryRadiative> stepper(
111+
grid, cfg.padding);
112+
stepper.set_gauge_parameters(params);
113+
114+
stepper.set_snapshot_callback([](const tensorium::tests::Grid &g, size_t step) {
115+
if (step % 10 == 0) {
116+
std::cout << ">> Exporting slice " << step << "..." << std::endl;
117+
export_slice_csv(g, step, "Output/viz");
118+
}
119+
});
120+
121+
double t = 0.0;
122+
123+
for (size_t n = 0; n <= cfg.steps; ++n) {
124+
const double dt = tensorium_RG::bssn::compute_dt_cfl(
125+
grid, tensorium::tests::make_cfl_control(cfg), cfg.padding);
126+
127+
stepper.step(grid, dt, n);
128+
t += dt;
129+
130+
std::printf("dt = %.4e\n", dt);
131+
std::printf("Step %zu / %zu (t=%.4f)\n", n, cfg.steps, t);
132+
}
133+
});
134+
REGISTER_TEST(
135+
"bssn.init.kerr_schild", "Print formatted BSSN fields after Kerr–Schild initialization", []() {
136+
tensorium::tests::StabilityRunConfig cfg;
137+
cfg.nx = 64;
138+
cfg.ny = 64;
139+
cfg.nz = 64;
140+
cfg.spacing = 0.1;
141+
cfg.ng = 4;
142+
cfg.steps = 60;
143+
cfg.cfl = 0.15;
144+
cfg.gauge_factor = 1.0;
145+
146+
tensorium_RG::fd::set_fd_dx(cfg.spacing);
147+
148+
tensorium::tests::Grid grid(cfg.nx, cfg.ny, cfg.nz, cfg.ng, cfg.spacing, cfg.spacing,
149+
cfg.spacing);
150+
151+
grid.x0 = -0.5 * cfg.spacing * cfg.nx + 0.5 * cfg.spacing;
152+
grid.y0 = -0.5 * cfg.spacing * cfg.ny + 0.5 * cfg.spacing;
153+
grid.z0 = -0.5 * cfg.spacing * cfg.nz + 0.5 * cfg.spacing;
154+
155+
tensorium_RG::init::kerr_schild_single(grid, 1.0, 0.0);
156+
157+
size_t I0, I1, J0, J1, K0, K1;
158+
grid.domain_bounds(I0, I1, J0, J1, K0, K1);
159+
160+
const size_t ic = I0 + grid.dims.nx / 2;
161+
const size_t jc = J0 + grid.dims.ny / 2;
162+
const size_t kc = K0 + grid.dims.nz / 2;
163+
tensorium_RG::bssn::GaugeParameters<double> params;
164+
params.eta = 3.0;
165+
params.beta_B_coeff = 0.5;
166+
167+
tensorium_RG::init::print_bssn_state_at(grid, ic, jc, kc, "Kerr–Schild single BH");
168+
tensorium_RG::bssn::BSSNRKStepper<double, tensorium_RG::bssn::BoundaryRadiative> stepper(
169+
grid, cfg.padding);
170+
stepper.set_gauge_parameters(params);
171+
172+
stepper.set_snapshot_callback([](const tensorium::tests::Grid &g, size_t step) {
173+
if (step % 1 == 0) {
174+
std::cout << ">> Exporting slice " << step << "..." << std::endl;
175+
export_slice_csv(g, step, "Output/viz");
176+
}
177+
});
178+
179+
double t = 0.0;
180+
181+
for (size_t n = 0; n <= cfg.steps; ++n) {
182+
const double dt = tensorium_RG::bssn::compute_dt_cfl(
183+
grid, tensorium::tests::make_cfl_control(cfg), cfg.padding);
184+
185+
stepper.step(grid, dt, n);
186+
t += dt;
187+
188+
std::printf("dt = %.4e\n", dt);
189+
std::printf("Step %zu / %zu (t=%.4f)\n", n, cfg.steps, t);
190+
}
191+
std::cout << "Kerr–Schild init test completed.\n";
192+
});

0 commit comments

Comments
 (0)