Skip to content

Commit a46a18a

Browse files
committed
test whether odeintegrator forces steps correctly
1 parent 9c73129 commit a46a18a

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

cpp/tests/test_numericalIntegration.cpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
* limitations under the License.
1919
*/
2020
#include "actions.h"
21+
#include "memilio/compartments/simulation.h"
2122
#include "memilio/math/euler.h"
2223
#include "memilio/math/adapt_rk.h"
2324
#include "memilio/math/stepper_wrapper.h"
2425
#include "memilio/utils/logging.h"
2526

26-
#include <gtest/gtest.h>
27-
#include <gmock/gmock.h>
27+
#include "gtest/gtest.h"
28+
#include "gmock/gmock.h"
2829

2930
#include <vector>
3031
#include <cmath>
@@ -418,3 +419,59 @@ TEST(TestOdeIntegrator, integratorContinuesAtLastState)
418419
integrator.advance(f, 4 * dt0, dt, result);
419420
integrator.advance(f, 5 * dt0, dt, result);
420421
}
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

Comments
 (0)