SIMLIB/C++ 3.09
Loading...
Searching...
No Matches
ni_fw.cc
Go to the documentation of this file.
1/////////////////////////////////////////////////////////////////////////////
2// ni_fw.cc
3//
4// Copyright (c) 1996-1997 David Leska
5// Copyright (c) 1998-2004 Petr Peringer
6//
7// This library is licensed under GNU Library GPL. See the file COPYING.
8//
9
10//
11// numerical integration: Fowler-Warten's method
12//
13
14////////////////////////////////////////////////////////////////////////////
15// interface
16//
17#include "simlib.h"
18#include "internal.h"
19#include "ni_fw.h"
20#include <cmath>
21#include <cfloat>
22#include <cstddef>
23
24
25////////////////////////////////////////////////////////////////////////////
26// implementation
27//
28namespace simlib3 {
29
31
32
33////////////////////////////////////////////////////////////////////////////
34// Fowler-Warten's method
35//
36/* Formula: Description needs graphic, see documentation. */
37
38void FW::Integrate(void)
39{
40 const int eul_max_count = 7; // avoids step growing too quickly for E
41 const double eul_err_coef = 1.0/150.0; // limits an error range
42 const double eul_step_coef = 0.25; // he = h * min_h
43 const double eul_step_rat = 0.01; // he = h * rat
44 const int fw_max_count = 7; // avoids step growing too quickly for FW
45 const double fw_err_coef = 1.0/150.0; // limits an error range
46 const double fw_err_rnghi = 1.5; // ranges for step accuracy
47 const double fw_err_rnglo = 0.75; // and error estimation
48 size_t i; // auxiliary variables for loops to go through list
49 Iterator ip, end_it; // of integrators
50 bool EulDoubleStepFlag; // allow increasing (doubling) the E. substepsize
51 bool FWDoubleStepFlag; // allow increasing (doubling) the FW. stepsize
52 bool FWHalveStepFlag; // allow reducing (halving) the FW. stepsize
53 bool FWMayDouble; // accuracy has been very good
54 // WARNING: following variables must be static!
55 // others are static only for efficiency
56 static int FWDoubleCount; // counter of doubling step requestes in FW
57 static int EulDoubleCount; // counter of doubling step requestes in Euler
58 static double Eul_StepSize; // step of Euler's method
59 static double PrevStep; // previous FW step
60
61 Dprintf((" Fowler-Warten integration step ")); // print debugging info
62 Dprintf((" Time = %g, optimal step = %g", (double)Time, OptStep));
63
64 end_it=LastIntegrator(); // end of container of integrators
65
66 FWDoubleStepFlag = true; // allow doubling for FW
67 EulDoubleStepFlag = true; // allow doubling for Euler
68 FWHalveStepFlag = false; // disable halving
69
70 if(FW_First) { // method is called first time -> authomatic start
71 FWDoubleCount = 0;
72 EulDoubleCount = 0;
73 Eul_StepSize = eul_step_rat*SIMLIB_StepSize;
74 }
75
76 //--------------------------------------------------------------------------
77 // Step of method
78 //--------------------------------------------------------------------------
79
80begin_step: // beginning of step
81
83 SIMLIB_ContractStepFlag = false; // clear reduce step flag
84 SIMLIB_ContractStep = 0.5*SIMLIB_StepSize; // reduce to half step
85
86 //--------------------------------------------------------------------------
87 // Substep of Euler's method
88 //--------------------------------------------------------------------------
89
90euler_step: // beginning of Euler's step
91
92 Eul_StepSize = max(Eul_StepSize, eul_step_coef*SIMLIB_MinStep); // low limit
93 Eul_StepSize = min(Eul_StepSize, eul_step_coef*SIMLIB_StepSize); // high
94
95 Dprintf(("E_MIN: %g, E_MAX %g", eul_step_coef*SIMLIB_MinStep,
96 eul_step_coef*SIMLIB_StepSize));
97
98 for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) {
99 // state y(t+he) = y + he * y'
100 (*ip)->SetState((*ip)->GetOldState()+Eul_StepSize*(*ip)->GetOldDiff());
101 }
102
103 _SetTime(Time,SIMLIB_StepStartTime + Eul_StepSize); // set time to t+he
104 SIMLIB_DeltaTime = Eul_StepSize;
105 SIMLIB_Dynamic(); // y'(t+he)=f(t+he, y(t+he))
106
107 //--------------------------------------------------------------------------
108 // Check on accuracy of Euler's integration, estimate error
109 //--------------------------------------------------------------------------
110
111 SIMLIB_ERRNO = 0; // OK
112 for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) {
113 double eerr; // estimated error
114 double terr; // greatest allowed error
115
116 // error estimation
117 eerr = Eul_StepSize*fabs((*ip)->GetDiff() - (*ip)->GetOldDiff());
118 terr = SIMLIB_AbsoluteError + fabs(SIMLIB_RelativeError*(*ip)->GetState());
119
120 if(eerr < eul_err_coef*terr) // tolerantion is fulfiled with provision
121 continue;
122
123 EulDoubleStepFlag = false; // disable increasing Eul_StepSize
124
125 if(eerr > terr) { // allowed tolerantion is overfulfiled
126 if(Eul_StepSize>eul_step_coef*SIMLIB_MinStep) { // reducing is possible
127 // halve Euler's step with limit
128 Eul_StepSize = max(0.5*Eul_StepSize, eul_step_coef*SIMLIB_MinStep);
129 goto euler_step; // compute again with smaller step
130 }
131 // reducing step is unpossible
132 SIMLIB_ERRNO++; // requested accuracy cannot be achieved
133 _Print("\n Integrator[%i] ",i);
134 }
135 if(SIMLIB_ConditionFlag) // event has been within the step
136 break;
137 } // for
138
139 if(SIMLIB_ERRNO) {
141 }
142
143 //--------------------------------------------------------------------------
144 // Analyse system at the end of the Euler's step
145 //--------------------------------------------------------------------------
146
147 if(StateCond()) { // check on state conditions changes within the step
148 goto begin_step;
149 }
150
151 //--------------------------------------------------------------------------
152 // Results of Euler's step have been accepted, take fresh step
153 //--------------------------------------------------------------------------
154
155 Dprintf(("E_F: %d, E_C: %d",EulDoubleStepFlag, EulDoubleCount));
156
157 // if accuracy has been good, increment counter
158 if(EulDoubleStepFlag) {
159 EulDoubleCount++;
160 } else {
161 EulDoubleCount = 0;
162 }
163 // if accuracy has been good max_count-times in previous steps,
164 // increase step for Euler's method
165 if(EulDoubleCount >= eul_max_count) {
166 EulDoubleCount = 0;
167 Eul_StepSize=min(eul_step_coef*SIMLIB_StepSize, 2.0*Eul_StepSize);
168 }
169
170 Dprintf(("E_S: %g", Eul_StepSize));
171
172 //--------------------------------------------------------------------------
173 // End of Euler's substep, FW continues
174 //--------------------------------------------------------------------------
175
176 for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) {
177 double yia; // formula's coefficients
178 double d1;
179 double d2;
180 double ll;
181 double c1;
182 double c0;
183 double denom;
184
185 yia = FW_First ? 0 : (((*ip)->GetOldState() - Y1[i]) / PrevStep);
186 d1 = (*ip)->GetOldDiff() - yia;
187 d2 = ((*ip)->GetDiff() - (*ip)->GetOldDiff())/Eul_StepSize;
188 ll = (d1<=prec && d1>=-prec) ? 0 : (d2/d1);
189 denom = SIMLIB_StepSize * ll;
190 c1 = (denom >= -prec)
191 ? (1.0 + 0.5 * denom)
192 : ((exp(denom) - 1.0) / denom);
193 c0 = (ll>=0) ? (1.0 + denom)
194 : exp(denom);
195 // state
196 (*ip)->SetState((*ip)->GetOldState() + SIMLIB_StepSize * (yia + c1 * d1));
197 ERR[i] = yia + c0 * d1;
198 }
199
202 SIMLIB_Dynamic(); // compute new state of model
203
204 //--------------------------------------------------------------------------
205 // Check on accuracy of FW integration, estimate error
206 //--------------------------------------------------------------------------
207
208 FWMayDouble = false;
209 SIMLIB_ERRNO = 0; // OK
210 for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) {
211 double eerr; // estimated error
212 double terr; // greatest allowed error
213
214 eerr = SIMLIB_StepSize*fabs((*ip)->GetDiff() - ERR[i]); // estimation
215 terr = SIMLIB_AbsoluteError + fabs(SIMLIB_RelativeError*(*ip)->GetState());
216
217 if(eerr > fw_err_rnghi*terr) {
218 // allowed tolerantion is overfulfiled,
219 // halve stepsize and compute again
220 FWDoubleStepFlag = false;
221 if(SIMLIB_StepSize > SIMLIB_MinStep) { // reducing step is possible
222 SIMLIB_OptStep = 0.5*SIMLIB_StepSize; // halve optimal step
223 if(SIMLIB_OptStep < SIMLIB_MinStep) { // limit of optimal step
225 }
227 IsEndStepEvent = false;
228 goto begin_step; // compute again with smaller step
229 }
230 // reducing step is unpossible
231 SIMLIB_ERRNO++; // requested accuracy cannot be achieved
232 _Print("\n Integrator[%lu] ",(unsigned long)i);
233 }
234 else if(eerr > fw_err_rnglo*terr) {
235 // accept results of current step,
236 // halve stepsize for succeeding steps
237 FWHalveStepFlag = true;
238 FWDoubleStepFlag = false;
239 }
240 else if(eerr < fw_err_coef*terr) {
241 // step may be doubled
242 FWMayDouble = true;
243 }
244
245 if(SIMLIB_ConditionFlag) // event has been within the step
246 break;
247
248 } // for
249
250 FWDoubleStepFlag = FWDoubleStepFlag && FWMayDouble;
251
252 if(SIMLIB_ERRNO) {
254 }
255
256 //--------------------------------------------------------------------------
257 // Analyse system at the end of the step
258 //--------------------------------------------------------------------------
259
260 if(StateCond()) { // check on changes of state conditions at end of step
261 goto begin_step;
262 }
263
264 //--------------------------------------------------------------------------
265 // Results of step have been accepted, store values and take fresh step
266 //--------------------------------------------------------------------------
267
268 for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) {
269 Y1[i] = (*ip)->GetOldDiff();
270 }
271 FW_First = false;
272 PrevStep = SIMLIB_StepSize;
273
274 // if accuracy hasn't been good, reduce step
275 if(FWHalveStepFlag) { // halving takes precedence over doubling
276 FWDoubleCount = 0;
278 Dprintf(("Reducing"));
279 } // if accuracy has been good, increase counter
280 else if(FWDoubleStepFlag) {
281 FWDoubleCount++;
282 Dprintf(("Incrementing"));
283 } else {
284 FWDoubleCount = 0;
285 Dprintf(("Clearing"));
286 }
287 // if accuracy has been good step_coef-times in previous steps,
288 // increase step for FW method
289 if(FWDoubleCount >= fw_max_count) {
290 FWDoubleCount = 0;
292 Dprintf(("Doubling"));
293 }
296 Dprintf(("Step: %g", SIMLIB_OptStep));
297
298} // FW::Integrate
299
300
301////////////////////////////////////////////////////////////////////////////
302// FW::PrepareStep
303// prepare object for integration step
304//
306{
307 Dprintf(("FW::PrepareStep()"));
308 // prepare inherited part
310 FW_First=true; // method will have to be re-initialized
311 return true; // some changes
312 }
313 return false; // no changes
314} // FW::PrepareStep
315
316
317////////////////////////////////////////////////////////////////////////////
318// initialize static class member
319//
320const double FW::prec = DBL_EPSILON; // near zero number
321
322}
323// end of ni_fw.cc
324
Memory Y1
Definition: ni_fw.h:25
virtual bool PrepareStep(void) override
prepare object for integration step
Definition: ni_fw.cc:305
virtual void Integrate(void) override
Definition: ni_fw.cc:38
static const double prec
Definition: ni_fw.h:27
bool FW_First
Definition: ni_fw.h:24
Memory ERR
Definition: ni_fw.h:25
static Iterator LastIntegrator(void)
Definition: simlib.h:1084
static bool IsEndStepEvent
Definition: simlib.h:1079
IntegratorContainer::iterator Iterator
Definition: simlib.h:1080
static bool StateCond(void)
check on changes of state conditions
Definition: numint.cc:175
virtual bool PrepareStep(void)
prepare object for integration step
Definition: numint.cc:282
static Iterator FirstIntegrator(void)
Definition: simlib.h:1081
@ AccuracyError
Definition: errors.h:23
Internal header file for SIMLIB/C++.
#define Dprintf(f)
Definition: internal.h:100
#define _SetTime(t, x)
macro for simple assignement to internal time variables
Definition: internal.h:228
Implementation of class CalendarList interface is static - using global functions in SQS namespace.
Definition: algloop.cc:32
const double & Time
model time (is NOT the block)
Definition: run.cc:48
bool SIMLIB_ConditionFlag
Definition: cond.cc:30
double SIMLIB_RelativeError
relative error
Definition: intg.cc:43
double SIMLIB_OptStep
optimal step
Definition: intg.cc:37
SIMLIB_IMPLEMENTATION
Definition: algloop.cc:34
bool SIMLIB_ContractStepFlag
requests shorter step
Definition: intg.cc:58
void SIMLIB_Dynamic()
performs evaluation of integrators and status blocks
Definition: continuous.cc:35
int _Print(const char *fmt,...)
output of messages to stdout, too
Definition: print.cc:68
double min(double a, double b)
Definition: internal.h:285
double max(double a, double b)
Definition: internal.h:286
void SIMLIB_warning(const enum _ErrEnum N)
print warning message and continue
Definition: error.cc:74
double SIMLIB_AbsoluteError
absolute error
Definition: intg.cc:42
double SIMLIB_MinStep
minimal step
Definition: intg.cc:38
double SIMLIB_MaxStep
max. step
Definition: intg.cc:39
int SIMLIB_ERRNO
Definition: intg.cc:32
const double & OptStep
optimal integration step
Definition: intg.cc:49
double SIMLIB_ContractStep
requested step size
Definition: intg.cc:59
double SIMLIB_StepStartTime
last step time
Definition: intg.cc:34
double SIMLIB_StepSize
actual step
Definition: intg.cc:40
double SIMLIB_DeltaTime
Time-SIMLIB_StepStartTime.
Definition: intg.cc:35
Fowler-Warten method.
Main SIMLIB/C++ interface.