- 3.0.2 core module.
IntegrationTest.h
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4 **********************************************************************************************************************/
5 
6 #pragma once
7 
8 #include <gtest/gtest.h>
9 #include <cmath>
10 #include <memory>
11 
12 
13 using namespace ct::core;
14 using std::shared_ptr;
15 
16 const size_t stateSize = 2;
17 bool plotResult = false;
18 
19 std::vector<std::string> integratorNames = {"euler", "rk4", "rk78", "rk5 variable", "ode45", "modified midpoint"};
20 std::vector<std::string> integratorCalls = {"const", "nSteps", "adaptive", "times"};
21 
22 double uniformRandomNumber(double min, double max)
23 {
24  std::random_device rd; // obtain a random number from hardware
25  std::mt19937 eng(rd()); // seed the generator
26  std::uniform_real_distribution<> distr(min, max); // define the range
27 
28  return distr(eng);
29 }
30 
31 void plotResults(std::vector<StateVectorArray<stateSize>>& stateTrajectories, std::vector<TimeArray>& timeTrajectories)
32 {
33 #ifdef PLOTTING_ENABLED
34 
35  plot::ion();
36  plot::figure();
37 
38  std::cout << "plotting " << stateTrajectories.size() << " trajectories" << std::endl;
39  for (size_t i = 0; i < stateTrajectories.size(); i++)
40  {
41  if (timeTrajectories[i].size() != stateTrajectories[i].size())
42  throw std::runtime_error("Cannot plot data, x and y not equal length");
43 
44  std::vector<double> position;
45  for (size_t j = 0; j < stateTrajectories[i].size(); j++)
46  {
47  position.push_back(stateTrajectories[i][j](0) + (stateTrajectories.size() - i - 1));
48  }
49 
50  int type = ((int)i) / 4;
51  int call = i % 4;
52  std::string name = integratorNames[type] + " - " + integratorCalls[call];
53 
54  plot::subplot(5, std::round(stateTrajectories.size() / 5.0) + 1, i + 1);
55  plot::plot(timeTrajectories[i].toImplementation(), position);
56  plot::title(name);
57  // std::cout << "time: "<< std::endl;
58  // for (auto k: timeTrajectories[i])
59  // std::cout << k << ' ';
60  //
61  // std::cout << "pos: "<< std::endl;
62  // for (auto k: position)
63  // std::cout << k << ' ';
64  }
65 
66  plot::show(false);
67 #endif
68 }
69 
70 TEST(IntegrationTest, derivativeTest)
71 {
72  try
73  {
74  // will be initialized later
75  shared_ptr<SecondOrderSystem> oscillator;
76 
77  // create a 2 state integrator
78  std::vector<std::shared_ptr<Integrator<stateSize>>> integrators;
79 
80  double dt = 0.0001;
81  size_t nTests = 5;
82 
83  for (size_t i = 0; i < nTests; i++)
84  {
85  // define integration times
86  Time startTime = 0.0;
87  Time finalTime = startTime + 1.0;
88  size_t nsteps = 1.0 / dt;
89 
90  // create an initial state
91  StateVector<stateSize> initialState;
92  initialState << 1.0, 0.0;
93 
94  // create a 10 Hz second order system with damping 0.1
95  double w_n = uniformRandomNumber(0, 10);
96  double zeta = uniformRandomNumber(0, 10);
97 
98  // make sure we are not complex
99  while (w_n * w_n - zeta * zeta <= 0)
100  {
101  w_n = uniformRandomNumber(0, 100);
102  zeta = uniformRandomNumber(0, 10);
103  }
104  oscillator = shared_ptr<SecondOrderSystem>(new SecondOrderSystem(w_n, zeta));
105  oscillator->checkParameters();
106  //oscillator->printSystemInfo();
107 
108  integrators.clear();
109  integrators.push_back(std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, EULER)));
110  integrators.push_back(std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, RK4)));
111  integrators.push_back(std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, RK78)));
112  integrators.push_back(
113  std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, RK5VARIABLE)));
114  integrators.push_back(std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, ODE45)));
115  integrators.push_back(
116  std::shared_ptr<Integrator<stateSize>>(new Integrator<stateSize>(oscillator, MODIFIED_MIDPOINT)));
117 
118 
119  // define analytical solution
120  // we start at 1 so we have 1/2 phase
121  double w_d = std::sqrt(w_n * w_n - zeta * zeta);
122  double phase = std::atan(w_d / zeta);
123  double A = 1.0 / std::sin(phase);
124  auto solution = [w_d, zeta, phase, A](
125  Time t) { return A * std::exp(-zeta * t) * std::sin(w_d * t + phase); };
126 
127 
128  // create trajectory containers
129  size_t integratorFunctions = 4; // integrate_const, integrate_n_steps, ...
130  size_t nIntegrators = integrators.size();
131  size_t nResults = nIntegrators * integratorFunctions;
132 
133  std::vector<StateVectorArray<stateSize>> stateTrajectories(nResults);
134  std::vector<TimeArray> timeTrajectories(nResults);
135  std::vector<StateVector<stateSize>> finalStates(nResults);
136 
137  StateVector<stateSize> initialStateLocal;
138 
139  for (size_t j = 0; j < nIntegrators; j++)
140  {
141  //std::cout << "Testing integrator: " << integratorNames[j] << std::endl;
142 
143  initialStateLocal = initialState;
144 
145  //std::cout << "Testing integration call const" << std::endl;
146 
147  // use fixed step integration
148  integrators[j]->integrate_const(initialStateLocal, startTime, finalTime, dt,
149  stateTrajectories[j * integratorFunctions + 0], timeTrajectories[j * integratorFunctions + 0]);
150 
151  initialStateLocal = initialState;
152 
153  //std::cout << "Testing integration call integrate_n_steps" << std::endl;
154 
155  integrators[j]->integrate_n_steps(initialStateLocal, startTime, nsteps, dt,
156  stateTrajectories[j * integratorFunctions + 1], timeTrajectories[j * integratorFunctions + 1]);
157 
158  initialStateLocal = initialState;
159 
160  //std::cout << "Testing integration call integrate adaptive" << std::endl;
161 
162  integrators[j]->integrate_adaptive(initialStateLocal, startTime, finalTime,
163  stateTrajectories[j * integratorFunctions + 2], timeTrajectories[j * integratorFunctions + 2], dt);
164 
165  initialStateLocal = initialState;
166 
167  for (size_t k = 0; k < nsteps + 1; k++)
168  {
169  timeTrajectories[j * integratorFunctions + 3].push_back(k * dt);
170  }
171 
172  //std::cout << "Testing integration call integrate times" << std::endl;
173 
174  integrators[j]->integrate_times(initialStateLocal, timeTrajectories[j * integratorFunctions + 3],
175  stateTrajectories[j * integratorFunctions + 3], dt);
176  }
177 
178  for (size_t j = 0; j < nResults; j++)
179  {
180  //std::cout << "Testing result number " << j << std::endl;
181 
182  // we should get at least two points, start and end
183  ASSERT_GT(stateTrajectories[j].size(), 2);
184  ASSERT_GT(timeTrajectories[j].size(), 2);
185 
186  // we should get equal number of states and times
187  ASSERT_LT((stateTrajectories[j].front() - initialState).array().abs().maxCoeff(), 1e-6);
188  ASSERT_EQ(stateTrajectories[j].size(), timeTrajectories[j].size());
189 
190  // start and end should be correct
191  ASSERT_NEAR(timeTrajectories[j].front(), startTime, dt);
192  ASSERT_NEAR(timeTrajectories[j].back(), finalTime, dt);
193 
194  // check ordering of time stamps
195  for (size_t k = 1; k < timeTrajectories[j].size(); k++)
196  {
197  ASSERT_GT(timeTrajectories[j][k], timeTrajectories[j][k - 1]);
198 
199  if (j % 4 == 0 || j % 4 == 1)
200  {
201  // check equidistance
202  ASSERT_NEAR(timeTrajectories[j][k] - timeTrajectories[j][k - 1], dt, 1e-6);
203  }
204  }
205 
206  // check correctness, only valid for non-adaptive
207  if (j % 4 - 2 != 0)
208  {
209  for (size_t k = 1; k < stateTrajectories[j].size(); k++)
210  {
211  double derivativeNumDiff = stateTrajectories[j][k](0) - stateTrajectories[j][k - 1](0);
212  double derivativeAnalytical =
213  solution(timeTrajectories[j][k]) - solution(timeTrajectories[j][k - 1]);
214 
215  ASSERT_NEAR(derivativeNumDiff, derivativeAnalytical, 1e-2);
216  }
217  }
218 
219  if (j > 1)
220  {
221  ASSERT_NEAR(stateTrajectories[j].back()(0), stateTrajectories[j - 1].back()(0), 5e-2);
222  }
223  }
224 
225  if (plotResult)
226  plotResults(stateTrajectories, timeTrajectories);
227  }
228  if (plotResult)
229  plot::show(true);
230  } catch (...)
231  {
232  std::cout << "Caught exception." << std::endl;
233  FAIL();
234  }
235 }
void plotResults(std::vector< StateVectorArray< stateSize >> &stateTrajectories, std::vector< TimeArray > &timeTrajectories)
Definition: IntegrationTest.h:31
An discrete array (vector) of a particular data type.
Definition: DiscreteArray.h:22
std::vector< std::string > integratorCalls
Definition: IntegrationTest.h:20
Definition: Integrator.h:32
bool ion()
Enable interactive mode.
Definition: plot.cpp:736
const double zeta
bool plotResult
Definition: IntegrationTest.h:17
Definition: Integrator.h:35
const double w_n
clear all close all load ct GNMSLog0 mat reformat t
const double dt
tpl::SecondOrderSystem< double > SecondOrderSystem
harmonic oscillator (double)
Definition: SecondOrderSystem.h:220
bool plot(const Eigen::Ref< const Eigen::MatrixXd > &x, const Eigen::Ref< const Eigen::MatrixXd > &y, const std::map< std::string, std::string > &keywords)
Create an x/y plot with properties as map.
Definition: plot.cpp:786
Definition: StateVector.h:12
Standard Integrator.
Definition: Integrator.h:62
for i
Definition: Integrator.h:33
TEST(IntegrationTest, derivativeTest)
Definition: IntegrationTest.h:70
void show(bool block=true)
Definition: plot.cpp:913
std::vector< std::string > integratorNames
Definition: IntegrationTest.h:19
const size_t stateSize
Definition: IntegrationTest.h:16
Definition: Integrator.h:37
Definition: Integrator.h:36
void title(const std::string &titlestr)
Definition: plot.cpp:883
double uniformRandomNumber(double min, double max)
Definition: IntegrationTest.h:22
bool subplot(const size_t nrows, const size_t ncols, const size_t plot_number)
Create a subplot.
Definition: plot.cpp:779
double Time
Definition: Time.h:11
Definition: Integrator.h:34
bool figure(std::string i="")
Create a new figure.
Definition: plot.cpp:744