WSF
WsfRungeKuttaOrbitalIntegrator.hpp
Go to the documentation of this file.
1// ****************************************************************************
2// CUI
3//
4// The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
5//
6// Copyright 2020 Infoscitex, a DCS Company. All rights reserved.
7//
8// The use, dissemination or disclosure of data in this file is subject to
9// limitation or restriction. See accompanying README and LICENSE for details.
10// ****************************************************************************
11
12#ifndef WSFRUNGEKUTTAORBITALINTEGRATOR_HPP
13#define WSFRUNGEKUTTAORBITALINTEGRATOR_HPP
14
15#include <algorithm>
16#include <array>
17#include <cmath>
18#include <limits>
19#include <stdexcept>
20#include <string>
21
22#include "UtCalendar.hpp"
23#include "UtInput.hpp"
24#include "UtLog.hpp"
28
29template<unsigned int Order, unsigned int StepCount, typename Integrator>
31{
32public:
35 ~WsfRungeKuttaOrbitalIntegrator() override = default;
36
37 static constexpr unsigned int cORDER = Order;
38 static constexpr unsigned int cSTEPCOUNT = StepCount;
39
46
47 double GetTolerance() const { return mTolerance; }
48 void SetTolerance(double aTolerance) { mTolerance = aTolerance; }
49 unsigned int GetMaxAdjustmentAttempts() const { return mMaxAdjustmentAttempts; }
50 void SetMaxAdjustmentAttempts(unsigned int aMaxAdjustmentAttempts)
51 {
52 mMaxAdjustmentAttempts = aMaxAdjustmentAttempts;
53 }
54 double GetMaxStepSize() const { return mMaxStepSize; }
55 void SetMaxStepSize(double aMaxStepSize) { mMaxStepSize = aMaxStepSize; }
56 double GetMinStepSize() const { return mMinStepSize; }
57 void SetMinStepSize(double aMinStepSize) { mMinStepSize = aMinStepSize; }
58 ErrorCriterion GetErrorCriterion() const { return mErrorCriterion; }
59 void SetErrorCriterion(ErrorCriterion aErrorCriterion) { mErrorCriterion = aErrorCriterion; }
60 double GetInitialStepSize() const { return mInitialStepSize; }
61 void SetInitialStepSize(double aInitialStepSize) { mInitialStepSize = aInitialStepSize; }
62
63 static std::string GetStringFromCriterion(ErrorCriterion aErrorCriterion)
64 {
65 std::string retval{};
66 switch (aErrorCriterion)
67 {
69 retval = "L_infinity";
70 break;
72 retval = "L_2";
73 break;
74 case ErrorCriterion::cUNKNOWN_CRITERION: // Intentional fall-through
75 default:
76 retval = "<UNKNOWN>";
77 break;
78 }
79 return retval;
80 }
81
82 static ErrorCriterion GetErrorCriterionFromString(const std::string& aErrorCriterion)
83 {
85 if (aErrorCriterion == "L_infinity")
86 {
88 }
89 else if (aErrorCriterion == "L_2")
90 {
92 }
93 return retval;
94 }
95
96 bool ProcessInput(UtInput& aInput) override
97 {
98 bool retval{true};
99 std::string command = aInput.GetCommand();
100 if (command == "tolerance")
101 {
102 aInput.ReadValue(mTolerance);
103 aInput.ValueGreater(mTolerance, 0.0);
104 }
105 else if (command == "max_adjustment_attempts")
106 {
107 aInput.ReadValue(mMaxAdjustmentAttempts);
108 aInput.ValueGreater(mMaxAdjustmentAttempts, 0u);
109 }
110 else if (command == "max_step_size")
111 {
112 aInput.ReadValue(mMaxStepSize);
113 aInput.ValueGreater(mMaxStepSize, 0.0);
114 if (mMaxStepSize < mMinStepSize)
115 {
116 throw UtInput::BadValue{aInput, "max_step_size must be larger than min_step_size."};
117 }
118 }
119 else if (command == "min_step_size")
120 {
121 aInput.ReadValue(mMinStepSize);
122 aInput.ValueGreaterOrEqual(mMinStepSize, 0.0);
123 if (mMinStepSize > mMaxStepSize)
124 {
125 throw UtInput::BadValue{aInput, "min_step_size must be smaller than max_step_size."};
126 }
127 }
128 else if (command == "error_criterion")
129 {
130 std::string criterion;
131 aInput.ReadValue(criterion);
132 mErrorCriterion = GetErrorCriterionFromString(criterion);
133 if (mErrorCriterion == ErrorCriterion::cUNKNOWN_CRITERION)
134 {
135 throw UtInput::BadValue(aInput, "Unknown error criterion.");
136 }
137 }
138 else if (command == "initial_step_size")
139 {
140 aInput.ReadValue(mInitialStepSize);
141 aInput.ValueGreater(mInitialStepSize, 0.0);
142 }
143 else
144 {
145 retval = false;
146 }
147 return retval;
148 }
149
150 ut::OrbitalState AdvanceToTime(const WsfOrbitalDynamics& aDynamics,
151 const UtCalendar& aFinalTime,
152 const ut::OrbitalState& aInitialState) override
153 {
154 ut::OrbitalState retval{aInitialState};
155
156 if (mStepSize < 0.0)
157 {
158 mStepSize = mInitialStepSize;
159 }
160
161 double finalTime = aFinalTime.GetTimeSince(retval.GetEpoch());
162 double currentTime{0.0};
163 bool acceptStep{false};
164 unsigned int attempts{0};
165
166 // Detect reversal of propagation direction.
167 if ((finalTime < 0.0 && mStepSize > 0.0) || (finalTime > 0.0 && mStepSize < 0.0))
168 {
169 mStepSize = -mStepSize;
170 }
171
172 while (fabs(currentTime) < fabs(finalTime))
173 {
174 if (fabs(mStepSize + currentTime) > fabs(finalTime))
175 {
176 mStepSize = finalTime - currentTime;
177 }
178
179 TakeStep(aDynamics, retval);
180 double error = ComputeError(retval.GetOrbitalStateVector());
181 if (error < mTolerance)
182 {
183 acceptStep = true;
184 }
185 else
186 {
187 ++attempts;
188 acceptStep = false;
189 }
190
191 if (attempts > mMaxAdjustmentAttempts)
192 {
193 if (!mWarned)
194 {
195 auto warn = ut::log::warning() << "Unable to find acceptable step size.";
196 warn.AddNote() << "Integration will proceed, but error will be outside tolerance.";
197 warn.AddNote() << "Attempts made: " << attempts;
198 warn.AddNote() << "Integrator type: " << Integrator::cTYPE;
199 mWarned = true;
200 }
201 acceptStep = true;
202 }
203
204 if (acceptStep)
205 {
206 AdvanceState(retval);
207 attempts = 0;
208 currentTime += mStepSize;
209 }
210
211 AdjustTimeStep(error);
212 }
213
214 return retval;
215 }
216
217private:
218 static double L_InfinityNorm(const UtVec3d& aVector)
219 {
220 return std::max(fabs(aVector[0]), std::max(fabs(aVector[1]), fabs(aVector[2])));
221 }
222
223 double ComputeError(const ut::OrbitalStateVector& aInitialOSV) const
224 {
225 double retval{};
226 switch (mErrorCriterion)
227 {
229 retval = std::max(L_InfinityNorm(mPosDiff), L_InfinityNorm(mVelDiff));
230 break;
232 {
233 UtVec3d stepPosDiff = mPredictedPosition - aInitialOSV.GetLocation();
234 double posMag2 = stepPosDiff.MagnitudeSquared();
235 double posError = mPosDiff.MagnitudeSquared();
236 if (posMag2 > 0.1)
237 {
238 posError /= posMag2;
239 }
240 posError = sqrt(posError);
241
242 UtVec3d stepVelDiff = mPredictedVelocity - aInitialOSV.GetVelocity();
243 double velMag2 = stepVelDiff.MagnitudeSquared();
244 double velError = mVelDiff.MagnitudeSquared();
245 if (velMag2 > 0.1)
246 {
247 velError /= velMag2;
248 }
249 velError = sqrt(velError);
250
251 retval = std::max(posError, velError);
252 }
253 break;
254 default:
255 throw std::runtime_error{"Invalid value for error criterion."};
256 break;
257 }
258 return retval;
259 }
260
261 // Implementation Note:
262 // The use of UtVec3d::operator+= was avoided after it was determined experimentally to impose
263 // a noticeable performance cost.
264
265 void TakeStep(const WsfOrbitalDynamics& aDynamics, const ut::OrbitalState& aCurrentState)
266 {
267 // 1) Compute the various RHS contributions.
268 // This method is First Same as Last (FSAL), so the saved velocity and acceleration
269 // give rhs[0].
270 auto& osv = aCurrentState.GetOrbitalStateVector();
271 mRHS_Position[0] = osv.GetVelocity();
272 mRHS_Velocity[0] = aCurrentState.GetAccelerationInertial();
273
274 for (unsigned int i = 1; i < Integrator::cSTEPCOUNT; ++i)
275 {
276 // Form the state based on prior prediction steps
277 mY_Position = osv.GetLocation();
278 mY_Velocity = osv.GetVelocity();
279 for (unsigned int j = 0; j < i; ++j)
280 {
281 double factor = mStepSize * Integrator::cAVALUES[i][j];
282
283 mY_Position[0] += mRHS_Position[j][0] * factor;
284 mY_Position[1] += mRHS_Position[j][1] * factor;
285 mY_Position[2] += mRHS_Position[j][2] * factor;
286 mY_Velocity[0] += mRHS_Velocity[j][0] * factor;
287 mY_Velocity[1] += mRHS_Velocity[j][1] * factor;
288 mY_Velocity[2] += mRHS_Velocity[j][2] * factor;
289 }
290
291 // Get the prediction time
292 UtCalendar predTime{aCurrentState.GetEpoch()};
293 predTime.AdvanceTimeBy(Integrator::cCVALUES[i] * mStepSize);
294
295 // Compute the RHS at the predicted time and location
296 mRHS_Position[i] = mY_Velocity;
297 mRHS_Velocity[i] =
298 aDynamics.ComputeAcceleration(mPropagatorPtr->GetDynamicalMass(), predTime, mY_Position, mY_Velocity);
299 }
300
301 // 2) Compute both predictions
302 mPredictedPosition = osv.GetLocation();
303 mPredictedVelocity = osv.GetVelocity();
304 mPosDiff.Set(0.0);
305 mVelDiff.Set(0.0);
306
307 for (unsigned int i = 0; i < Integrator::cSTEPCOUNT; ++i)
308 {
309 double bFactor = mStepSize * Integrator::cBVALUES[i];
310 double bErrorFactor = mStepSize * Integrator::cERRORVALUES[i];
311
312 mPredictedPosition[0] += mRHS_Position[i][0] * bFactor;
313 mPredictedPosition[1] += mRHS_Position[i][1] * bFactor;
314 mPredictedPosition[2] += mRHS_Position[i][2] * bFactor;
315 mPredictedVelocity[0] += mRHS_Velocity[i][0] * bFactor;
316 mPredictedVelocity[1] += mRHS_Velocity[i][1] * bFactor;
317 mPredictedVelocity[2] += mRHS_Velocity[i][2] * bFactor;
318 mPosDiff[0] += mRHS_Position[i][0] * bErrorFactor;
319 mPosDiff[1] += mRHS_Position[i][1] * bErrorFactor;
320 mPosDiff[2] += mRHS_Position[i][2] * bErrorFactor;
321 mVelDiff[0] += mRHS_Velocity[i][0] * bErrorFactor;
322 mVelDiff[1] += mRHS_Velocity[i][1] * bErrorFactor;
323 mVelDiff[2] += mRHS_Velocity[i][2] * bErrorFactor;
324 }
325 }
326
327 void AdjustTimeStep(double aError)
328 {
329 if (aError > mTolerance)
330 {
331 mStepSize *= 0.9 * pow(mTolerance / aError, 1.0 / (Integrator::cORDER - 1.0));
332 }
333 else
334 {
335 mStepSize *= 0.9 * pow(mTolerance / aError, 1.0 / Integrator::cORDER);
336 }
337 if (fabs(mStepSize) > mMaxStepSize)
338 {
339 if (mStepSize > 0.0)
340 {
341 mStepSize = mMaxStepSize;
342 }
343 else
344 {
345 mStepSize = -mMaxStepSize;
346 }
347 }
348 if (fabs(mStepSize) < mMinStepSize)
349 {
350 if (!mWarned)
351 {
352 auto warn = ut::log::warning() << "Timestep limited by minimum step size.";
353 warn.AddNote() << "Integration will proceed, but will be outside error tolerance";
354 warn.AddNote() << "Adjusted timestep: " << mStepSize;
355 warn.AddNote() << "Minimum timestep: " << mMinStepSize;
356 warn.AddNote() << "Integrator: " << Integrator::cTYPE;
357 mWarned = true;
358 }
359 if (mStepSize > 0.0)
360 {
361 mStepSize = mMinStepSize;
362 }
363 else
364 {
365 mStepSize = -mMinStepSize;
366 }
367 }
368 }
369
370 void AdvanceState(ut::OrbitalState& aCurrentState)
371 {
372 auto epoch = aCurrentState.GetEpoch();
373 epoch.AdvanceTimeBy(mStepSize);
374 aCurrentState.Set(epoch, ut::OrbitalStateVector{mPredictedPosition, mPredictedVelocity});
375 aCurrentState.SetAccelerationInertial(mRHS_Velocity[Integrator::cSTEPCOUNT - 1]);
376 }
377
378 double mStepSize{-1.0};
379 double mTolerance{1.0e-10};
380 double mMaxStepSize{std::numeric_limits<double>::max()};
381 double mMinStepSize{0.0};
382 double mInitialStepSize{0.1};
383 unsigned int mMaxAdjustmentAttempts{50};
385 std::array<UtVec3d, StepCount> mRHS_Position{};
386 std::array<UtVec3d, StepCount> mRHS_Velocity{};
387 UtVec3d mY_Position{};
388 UtVec3d mY_Velocity{};
389 UtVec3d mPredictedPosition{};
390 UtVec3d mPredictedVelocity{};
391 UtVec3d mPosDiff{};
392 UtVec3d mVelDiff{};
393 bool mWarned{false};
394};
395
396#endif // WSFRUNGEKUTTAORBITALINTEGRATOR_HPP
Definition WsfOrbitalDynamics.hpp:32
UtVec3d ComputeAcceleration(double aMass, const UtCalendar &aTime, const UtVec3d &aPosition, const UtVec3d &aVelocity) const
Definition WsfOrbitalDynamics.cpp:83
const WsfIntegratingPropagator * mPropagatorPtr
Definition WsfOrbitalIntegrator.hpp:51
WsfOrbitalIntegrator()=default
~WsfRungeKuttaOrbitalIntegrator() override=default
double GetMaxStepSize() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:54
static ErrorCriterion GetErrorCriterionFromString(const std::string &aErrorCriterion)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:82
void SetMinStepSize(double aMinStepSize)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:57
void SetMaxStepSize(double aMaxStepSize)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:55
static constexpr unsigned int cORDER
Definition WsfRungeKuttaOrbitalIntegrator.hpp:37
void SetInitialStepSize(double aInitialStepSize)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:61
void SetMaxAdjustmentAttempts(unsigned int aMaxAdjustmentAttempts)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:50
ErrorCriterion
Definition WsfRungeKuttaOrbitalIntegrator.hpp:41
@ cL_TWO_NORM
Definition WsfRungeKuttaOrbitalIntegrator.hpp:44
@ cUNKNOWN_CRITERION
Definition WsfRungeKuttaOrbitalIntegrator.hpp:42
@ cL_INFINITY_NORM
Definition WsfRungeKuttaOrbitalIntegrator.hpp:43
double GetTolerance() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:47
unsigned int GetMaxAdjustmentAttempts() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:49
ut::OrbitalState AdvanceToTime(const WsfOrbitalDynamics &aDynamics, const UtCalendar &aFinalTime, const ut::OrbitalState &aInitialState) override
Definition WsfRungeKuttaOrbitalIntegrator.hpp:150
void SetTolerance(double aTolerance)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:48
void SetErrorCriterion(ErrorCriterion aErrorCriterion)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:59
double GetInitialStepSize() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:60
WsfRungeKuttaOrbitalIntegrator(const WsfRungeKuttaOrbitalIntegrator &aOther)=default
bool ProcessInput(UtInput &aInput) override
Definition WsfRungeKuttaOrbitalIntegrator.hpp:96
ErrorCriterion GetErrorCriterion() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:58
double GetMinStepSize() const
Definition WsfRungeKuttaOrbitalIntegrator.hpp:56
static constexpr unsigned int cSTEPCOUNT
Definition WsfRungeKuttaOrbitalIntegrator.hpp:38
static std::string GetStringFromCriterion(ErrorCriterion aErrorCriterion)
Definition WsfRungeKuttaOrbitalIntegrator.hpp:63
Copyrights Multiple, All Rights Reserved