|
18 | 18 | * limitations under the License. |
19 | 19 | */ |
20 | 20 | #include "actions.h" |
| 21 | +#include "memilio/compartments/simulation.h" |
21 | 22 | #include "memilio/math/euler.h" |
22 | 23 | #include "memilio/math/adapt_rk.h" |
23 | 24 | #include "memilio/math/stepper_wrapper.h" |
24 | 25 | #include "memilio/utils/logging.h" |
25 | 26 |
|
26 | | -#include <gtest/gtest.h> |
27 | | -#include <gmock/gmock.h> |
| 27 | +#include "gtest/gtest.h" |
| 28 | +#include "gmock/gmock.h" |
28 | 29 |
|
29 | 30 | #include <vector> |
30 | 31 | #include <cmath> |
@@ -418,3 +419,59 @@ TEST(TestOdeIntegrator, integratorContinuesAtLastState) |
418 | 419 | integrator.advance(f, 4 * dt0, dt, result); |
419 | 420 | integrator.advance(f, 5 * dt0, dt, result); |
420 | 421 | } |
| 422 | + |
| 423 | +TEST(TestOdeIntegrator, integratorForcesLastStepSize) |
| 424 | +{ |
| 425 | + using testing::_; |
| 426 | + using testing::Eq; |
| 427 | + |
| 428 | + const double dt_min = 0.7; |
| 429 | + const double t_max = 3.0; // this must not be intiger divisable by dt_min |
| 430 | + const auto mock_core = std::make_shared<testing::StrictMock<MockIntegratorCore>>(); |
| 431 | + auto integrator = mio::OdeIntegrator<double>(mock_core); |
| 432 | + auto y0 = Eigen::VectorXd::Constant(1, 0); |
| 433 | + mio::TimeSeries<double> mock_result(0, y0), ref_result(0, y0); |
| 434 | + auto f = [](auto&&, auto&&, auto&&) {}; |
| 435 | + auto step_fct = [&](auto&&, auto&&, auto&& t, auto&& dt, auto&&, auto&& force_step_size) { |
| 436 | + if (!force_step_size) { |
| 437 | + auto dt_in = dt; |
| 438 | + dt = std::max(dt, dt_min); |
| 439 | + t += dt; |
| 440 | + return dt_in >= dt_min; |
| 441 | + } |
| 442 | + else { |
| 443 | + t += dt; |
| 444 | + return true; |
| 445 | + } |
| 446 | + }; |
| 447 | + |
| 448 | + // run a mock integration to examine whether only the last step is forced |
| 449 | + |
| 450 | + { |
| 451 | + testing::InSequence seq; |
| 452 | + EXPECT_CALL(*mock_core, step(_, _, _, _, _, Eq(false))) // make time steps of size dt_min |
| 453 | + .Times(int(t_max / dt_min) + 1) |
| 454 | + .WillRepeatedly(testing::Invoke(step_fct)); |
| 455 | + EXPECT_CALL(*mock_core, step(_, _, _, _, _, Eq(true))) // make the last step with dt < dt_min |
| 456 | + .Times(1) |
| 457 | + .WillOnce(testing::Invoke(step_fct)); |
| 458 | + } |
| 459 | + double dt = 0.5; // this is on purpose < dt_min |
| 460 | + integrator.advance(f, t_max, dt, mock_result); |
| 461 | + |
| 462 | + // run the same integration with a "real" core to check that the step_fct is not outdated |
| 463 | + |
| 464 | + mio::set_log_level(mio::LogLevel::off); |
| 465 | + const double tol = 1.0; // large enough such that it should not affect the test |
| 466 | + dt = 0.5; // this is on purpose < dt_min |
| 467 | + integrator.set_integrator(std::make_shared<mio::DefaultIntegratorCore<double>>(tol, tol, dt_min, dt_min)); |
| 468 | + // note that the core is forced by dt_min==dt_max to make the same time step of dt_min, up until the last |
| 469 | + integrator.advance(f, t_max, dt, ref_result); |
| 470 | + mio::set_log_level(mio::LogLevel::warn); |
| 471 | + |
| 472 | + // compare time steps taken by the mock step_fct and DefaultIntegratorCore::step |
| 473 | + ASSERT_DOUBLE_EQ(mock_result.get_num_time_points(), ref_result.get_num_time_points()); |
| 474 | + for (Eigen::Index i = 0; i < mock_result.get_num_time_points(); i++) { |
| 475 | + EXPECT_DOUBLE_EQ(mock_result.get_time(i), ref_result.get_time(i)); |
| 476 | + } |
| 477 | +} |
0 commit comments