JET_END_TEST_F

JET_BEGIN_TEST_F(FmmLevelSetSolver2, Extrapolate) {
    Size2 size(160, 120);
    Vector2D gridSpacing(1.0/size.x, 1.0/size.x);
    double maxDistance = 20.0 * gridSpacing.x;

    FmmLevelSetSolver2 solver;
    CellCenteredScalarGrid2 sdf(size, gridSpacing);
    CellCenteredScalarGrid2 input(size, gridSpacing);
    CellCenteredScalarGrid2 output(size, gridSpacing);

    sdf.fill([&](const Vector2D& x) {
        return (x - Vector2D(0.75, 0.5)).length() - 0.3;
    });

    input.fill([&](const Vector2D& x) {
        if ((x - Vector2D(0.75, 0.5)).length() <= 0.3) {
            double p = 10.0 * kPiD;
            return 0.5 * 0.25 * std::sin(p * x.x) * std::sin(p * x.y);
        } else {
            return 0.0;
        }
    });

    solver.extrapolate(input, sdf, maxDistance, &output);

    saveData(sdf.constDataAccessor(), "sdf_#grid2,iso.npy");
    saveData(input.constDataAccessor(), "input_#grid2.npy");
    saveData(output.constDataAccessor(), "output_#grid2.npy");
}
JET_END_TEST_F

JET_BEGIN_TEST_F(FmmLevelSetSolver2, Reinitialize) {
    CellCenteredScalarGrid2 sdf(160, 120), temp(160, 120);
    FmmLevelSetSolver2 solver;

    // Starting from constant field
    sdf.fill([](const Vector2D& x) {
        return 1.0;
    });
    saveData(sdf.constDataAccessor(), "constant0_#grid2,iso.npy");

    solver.reinitialize(sdf, 160.0, &temp);

    saveData(temp.constDataAccessor(), "constant1_#grid2,iso.npy");

    // Starting from SDF field
    sdf.fill([](const Vector2D& x) {
        return (x - Vector2D(80, 80)).length() - 32.0;
    });
    saveData(sdf.constDataAccessor(), "sdf0_#grid2,iso.npy");

    solver.reinitialize(sdf, 160.0, &temp);

    saveData(temp.constDataAccessor(), "sdf1_#grid2,iso.npy");

    // Starting from scaled SDF field
    sdf.fill([](const Vector2D& x) {
        double r = (x - Vector2D(80, 80)).length() - 32.0;
        return 2.0 * r;
    });
    saveData(sdf.constDataAccessor(), "scaled0_#grid2,iso.npy");

    solver.reinitialize(sdf, 160.0, &temp);

    saveData(temp.constDataAccessor(), "scaled1_#grid2,iso.npy");

    // Starting from scaled SDF field
    sdf.fill([](const Vector2D& x) {
        double r = (x - Vector2D(80, 80)).length() - 32.0;
        return (r < 0.0) ? -0.5 : 0.5;
    });
    saveData(sdf.constDataAccessor(), "unit_step0_#grid2,iso.npy");

    solver.reinitialize(sdf, 160.0, &temp);

    saveData(temp.constDataAccessor(), "unit_step1_#grid2,iso.npy");
}
void LevelSetLiquidSolver2::extrapolateVelocityToAir(double currentCfl) {
    auto sdf = signedDistanceField();
    auto vel = gridSystemData()->velocity();

    auto u = vel->uAccessor();
    auto v = vel->vAccessor();
    auto uPos = vel->uPosition();
    auto vPos = vel->vPosition();

    Array2<char> uMarker(u.size());
    Array2<char> vMarker(v.size());

    uMarker.parallelForEachIndex([&](size_t i, size_t j) {
        if (isInsideSdf(sdf->sample(uPos(i, j)))) {
            uMarker(i, j) = 1;
        } else {
            uMarker(i, j) = 0;
            u(i, j) = 0.0;
        }
    });

    vMarker.parallelForEachIndex([&](size_t i, size_t j) {
        if (isInsideSdf(sdf->sample(vPos(i, j)))) {
            vMarker(i, j) = 1;
        } else {
            vMarker(i, j) = 0;
            v(i, j) = 0.0;
        }
    });

    const Vector2D gridSpacing = sdf->gridSpacing();
    const double h = std::max(gridSpacing.x, gridSpacing.y);
    const double maxDist
        = std::max(2.0 * currentCfl, _minReinitializeDistance) * h;

    JET_INFO << "Max velocity extrapolation distance: " << maxDist;

    FmmLevelSetSolver2 fmmSolver;
    fmmSolver.extrapolate(*vel, *sdf, maxDist, vel.get());

    applyBoundaryCondition();
}