TEST(NumLib, TimeSteppingIterationNumberBased1)
{
    std::vector<std::size_t> iter_times_vector = {0, 3, 5, 7};
    std::vector<double> multiplier_vector = {2.0, 1.0, 0.5, 0.25};
    NumLib::IterationNumberBasedAdaptiveTimeStepping alg(1, 31, 1, 10, 1, iter_times_vector, multiplier_vector);

    ASSERT_TRUE(alg.next()); // t=2, dt=1
    NumLib::TimeStep ts = alg.getTimeStep();
    ASSERT_EQ(1u, ts.steps());
    ASSERT_EQ(1., ts.previous());
    ASSERT_EQ(2., ts.current());
    ASSERT_EQ(1., ts.dt());
    ASSERT_TRUE(alg.accepted());

    ASSERT_TRUE(alg.next()); // t=4, dt=2

    // dt*=2
    alg.setNIterations(3);
    ASSERT_TRUE(alg.next()); // t=8, dt=4
    ts = alg.getTimeStep();
    ASSERT_EQ(3u, ts.steps());
    ASSERT_EQ(4., ts.previous());
    ASSERT_EQ(8., ts.current());
    ASSERT_EQ(4., ts.dt());
    ASSERT_TRUE(alg.accepted());

    // dt*=1
    alg.setNIterations(5);
    ASSERT_TRUE(alg.next()); // t=12, dt=4
    ts = alg.getTimeStep();
    ASSERT_EQ(4u, ts.steps());
    ASSERT_EQ(8., ts.previous());
    ASSERT_EQ(12., ts.current());
    ASSERT_EQ(4., ts.dt());
    ASSERT_TRUE(alg.accepted());

    // dt*=0.5
    alg.setNIterations(7);
    ASSERT_TRUE(alg.next()); // t=14, dt=2
    ts = alg.getTimeStep();
    ASSERT_EQ(5u, ts.steps());
    ASSERT_EQ(12., ts.previous());
    ASSERT_EQ(14., ts.current());
    ASSERT_EQ(2., ts.dt());
    ASSERT_TRUE(alg.accepted());

    // dt*=0.25 but dt_min = 1
    alg.setNIterations(8); // exceed max
    ASSERT_TRUE(alg.next()); // t=13, dt=1
    ts = alg.getTimeStep();
    ASSERT_EQ(5u, ts.steps());
    ASSERT_EQ(12., ts.previous());
    ASSERT_EQ(13, ts.current());
    ASSERT_EQ(1., ts.dt());
    ASSERT_FALSE(alg.accepted());

    // restart, dt*=1
    alg.setNIterations(4);
    ASSERT_TRUE(alg.next()); // t=14, dt=1
    ts = alg.getTimeStep();
    ASSERT_EQ(6u, ts.steps());
    ASSERT_EQ(13., ts.previous());
    ASSERT_EQ(14, ts.current());
    ASSERT_EQ(1., ts.dt());
    ASSERT_TRUE(alg.accepted());
}
TEST(NumLibTimeStepping, testEvolutionaryPIDcontroller)
{
    const char xml[] =
        "<time_stepping>"
        "   <type>EvolutionaryPIDcontroller</type>"
        "   <t_initial> 0.0 </t_initial>"
        "   <t_end> 10 </t_end>"
        "   <dt_guess> 0.01 </dt_guess>"
        "   <dt_min> 0.001 </dt_min>"
        "   <dt_max> 1 </dt_max>"
        "   <rel_dt_min> 0.01 </rel_dt_min>"
        "   <rel_dt_max> 5 </rel_dt_max>"
        "   <tol> 1.e-3 </tol>"
        "</time_stepping>";
    auto const PIDStepper = createTestTimeStepper(xml);

    double solution_error = 0.;
    int const number_iterations = 0;
    // 1st step
    ASSERT_TRUE(PIDStepper->next(solution_error, number_iterations));
    NumLib::TimeStep ts = PIDStepper->getTimeStep();
    double h_new = 0.01;
    double t_previous = 0.;
    ASSERT_EQ(1u, ts.steps());
    ASSERT_EQ(t_previous, ts.previous());
    ASSERT_EQ(t_previous + h_new, ts.current());
    ASSERT_EQ(h_new, ts.dt());
    ASSERT_TRUE(PIDStepper->accepted());
    t_previous += h_new;

    // e_n_minus1 is filled.
    solution_error = 1.0e-4;
    PIDStepper->next(solution_error, number_iterations);
    ts = PIDStepper->getTimeStep();
    h_new = ts.dt();
    ASSERT_EQ(2u, ts.steps());
    const double tol = 1.e-16;
    ASSERT_NEAR(t_previous, ts.previous(), tol);
    ASSERT_NEAR(t_previous + h_new, ts.current(), tol);
    ASSERT_TRUE(PIDStepper->accepted());
    t_previous += h_new;

    // e_n_minus2 is filled.
    solution_error = 0.5e-3;
    PIDStepper->next(solution_error, number_iterations);
    ts = PIDStepper->getTimeStep();
    h_new = ts.dt();
    ASSERT_EQ(3u, ts.steps());
    ASSERT_NEAR(t_previous, ts.previous(), tol);
    ASSERT_NEAR(t_previous + h_new, ts.current(), tol);
    ASSERT_TRUE(PIDStepper->accepted());

    // error > TOL=1.3-3, step rejected and new step size estimated.
    solution_error = 0.01;
    PIDStepper->next(solution_error, number_iterations);
    ts = PIDStepper->getTimeStep();
    h_new = ts.dt();
    // No change in ts.steps
    ASSERT_EQ(3u, ts.steps());
    // No change in ts.previous(), which is the same as that of the previous
    // step.
    ASSERT_NEAR(t_previous, ts.previous(), tol);
    ASSERT_NEAR(t_previous + h_new, ts.current(), tol);
    ASSERT_FALSE(PIDStepper->accepted());
    t_previous += h_new;

    // With e_n, e_n_minus1, e_n_minus2
    solution_error = 0.4e-3;
    PIDStepper->next(solution_error, number_iterations);
    ts = PIDStepper->getTimeStep();
    h_new = ts.dt();
    ASSERT_EQ(4u, ts.steps());
    ASSERT_NEAR(t_previous, ts.previous(), tol);
    ASSERT_NEAR(t_previous + h_new, ts.current(), tol);
    ASSERT_TRUE(PIDStepper->accepted());
}