diff --git a/models/dynamics/CMakeLists.txt b/models/dynamics/CMakeLists.txt index 31e4940d..b32e663d 100644 --- a/models/dynamics/CMakeLists.txt +++ b/models/dynamics/CMakeLists.txt @@ -105,5 +105,6 @@ add_cml_tests( dynamics SOURCES + integration/accumulated_absolute_deltas/test/accumulated_absolute_deltas_test.cc mass_derivative_dynamics/tests/test_mass_derivative_dynamics.cpp ) diff --git a/models/dynamics/integration/accumulated_absolute_deltas/include/accumulated_absolute_deltas.hh b/models/dynamics/integration/accumulated_absolute_deltas/include/accumulated_absolute_deltas.hh index 0f7295bd..f3f9e975 100644 --- a/models/dynamics/integration/accumulated_absolute_deltas/include/accumulated_absolute_deltas.hh +++ b/models/dynamics/integration/accumulated_absolute_deltas/include/accumulated_absolute_deltas.hh @@ -34,6 +34,8 @@ PROGRAMMERS: #include "cml/models/utilities/subscriptions/include/subscriptions.hh" +#include + class AccumulatedAbsoluteDeltas : public SubscriptionBase { protected: @@ -48,6 +50,9 @@ class AccumulatedAbsoluteDeltas : public SubscriptionBase and old_variable values */ AccumulatedAbsoluteDeltas( const double * variable_, const unsigned int size_); + template + explicit AccumulatedAbsoluteDeltas(const double (&variable_)[size_]) + : AccumulatedAbsoluteDeltas(variable_, size_) {} ~AccumulatedAbsoluteDeltas() override; AccumulatedAbsoluteDeltas ( const AccumulatedAbsoluteDeltas &) = delete; AccumulatedAbsoluteDeltas & operator= ( const AccumulatedAbsoluteDeltas &) = delete; diff --git a/models/dynamics/integration/accumulated_absolute_deltas/src/accumulated_absolute_deltas.cc b/models/dynamics/integration/accumulated_absolute_deltas/src/accumulated_absolute_deltas.cc index 085e607c..3b25dafe 100644 --- a/models/dynamics/integration/accumulated_absolute_deltas/src/accumulated_absolute_deltas.cc +++ b/models/dynamics/integration/accumulated_absolute_deltas/src/accumulated_absolute_deltas.cc @@ -11,8 +11,8 @@ LIBRARY DEPENDENCIES: (((Gary Turner) (OSR) (Apr 2017) (Antares) (new))) **********************************************************************/ +#include #include -#include #include "../include/accumulated_absolute_deltas.hh" #include "cml/models/utilities/cml_message/include/cml_message.hh" @@ -33,7 +33,7 @@ AccumulatedAbsoluteDeltas::AccumulatedAbsoluteDeltas( if (variable == nullptr) { CMLMessage::fail( __FILE__,__LINE__,"Invalid construction\n", - "The variable being accumulated in NULL.\n"); + "The variable being accumulated is NULL.\n"); } if (size == 0) { CMLMessage::fail( @@ -44,6 +44,8 @@ AccumulatedAbsoluteDeltas::AccumulatedAbsoluteDeltas( accumulated_deltas = new double[size]; old_variable = new double[size]; + + std::fill_n(accumulated_deltas, size, 0.0); } /***************************************************************************** @@ -90,10 +92,8 @@ Purpose:(resets the accumulated value and the starting values) void AccumulatedAbsoluteDeltas::reset() { - for (unsigned int ii = 0; ii < size; ++ii) { - old_variable[ii] = variable[ii]; - accumulated_deltas[ii] = 0.0; - } + std::copy_n(variable, size, old_variable); + std::fill_n(accumulated_deltas, size, 0.0); } /***************************************************************************** @@ -110,4 +110,4 @@ AccumulatedAbsoluteDeltas::update() accumulated_deltas[ii] += std::abs( variable[ii] - old_variable[ii]); old_variable[ii] = variable[ii]; } -} \ No newline at end of file +} diff --git a/models/dynamics/integration/accumulated_absolute_deltas/test/accumulated_absolute_deltas_test.cc b/models/dynamics/integration/accumulated_absolute_deltas/test/accumulated_absolute_deltas_test.cc new file mode 100644 index 00000000..b93b6df5 --- /dev/null +++ b/models/dynamics/integration/accumulated_absolute_deltas/test/accumulated_absolute_deltas_test.cc @@ -0,0 +1,98 @@ +#include "../include/accumulated_absolute_deltas.hh" +#include "cml/models/utilities/cml_message/include/cml_message.hh" +#include "mocks/cml/cml_message_mock.hh" + +#include +#include + +namespace { + +inline constexpr double tolerance = 1e-12; // Float comparison tolerance + +TEST(AccumulatedAbsoluteDeltas, Construction) { + using testing::_; + using testing::HasSubstr; + + const double variable[3] {}; + CMLMessage::Mock cml_message_mock; + + // Test attempting to construct with a null pointer. + EXPECT_CALL( + cml_message_mock, + publish(CMLMessage::Fail, _, _, HasSubstr("The variable being accumulated is NULL"))); + const AccumulatedAbsoluteDeltas null_test(nullptr, 3); + + // Test attempting to construct with an array of size 0. + EXPECT_CALL( + cml_message_mock, + publish(CMLMessage::Fail, _, _, HasSubstr("The size of the array must be positive"))); + const AccumulatedAbsoluteDeltas zero_test(variable, 0); + + // Test normal construction. + const AccumulatedAbsoluteDeltas valid1(variable, std::size(variable)); + const AccumulatedAbsoluteDeltas valid2(variable); + EXPECT_NE(valid1.accumulated_deltas, nullptr); + EXPECT_NE(valid2.accumulated_deltas, nullptr); +} + +TEST(AccumulatedAbsoluteDeltas, Disabled) { + double data = 0.0; + AccumulatedAbsoluteDeltas article(&data, 1); + article.disable(); + + // Attempt to initialize the model while it's disabled. + article.initialize(); + EXPECT_FALSE(article.is_initialized()); + + // The data changes but since the model is disabled, it will not accumulate + // the deltas. + data = 1.0; + article.update(); + EXPECT_EQ(article.accumulated_deltas[0], 0.0); +} + +TEST(AccumulatedAbsoluteDeltas, Update) { + double data[2] {1.0, -1.0}; + AccumulatedAbsoluteDeltas article(data); + article.subscribe(); + article.initialize(); + + // Test a few updates. + // Update 1: + // Element 0 changes to 0.1: net change = 0.1 + // Element 1 stays the same: net change = 0 + data[0] = 1.1; + data[1] = -1.0; + article.update(); + EXPECT_NEAR(article.accumulated_deltas[0], 0.1, tolerance); + EXPECT_NEAR(article.accumulated_deltas[1], 0.0, tolerance); + + // Update 2: + // Element 0 changes to 0: net change = 0.1 + (1.1 - 0) = 1.2 + // Element 1 changes to 0: net change = 1 + data[0] = 0.0; + data[1] = 0.0; + article.update(); + EXPECT_NEAR(article.accumulated_deltas[0], 1.2, tolerance); + EXPECT_NEAR(article.accumulated_deltas[1], 1.0, tolerance); + + // Update 3: + // Element 0 changes to -5: net change = 1.2 + 5 = 6.2 + // Element 1 changes to 10: net change = 1 + 10 = 11 + data[0] = -5.0; + data[1] = 10.0; + article.update(); + EXPECT_NEAR(article.accumulated_deltas[0], 6.2, tolerance); + EXPECT_NEAR(article.accumulated_deltas[1], 11.0, tolerance); + + // The model is deactivated. The data it's accumulating changes but the + // accumulated deltas remain the same. + article.unsubscribe(); + data[0] = 0.0; + data[1] = 0.0; + article.update(); + EXPECT_NEAR(article.accumulated_deltas[0], 6.2, tolerance); + EXPECT_NEAR(article.accumulated_deltas[1], 11.0, tolerance); +} + +} // namespace