SIMLIB/C++  3.07
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 //
28 namespace simlib3 {
29 
31 
32 
33 ////////////////////////////////////////////////////////////////////////////
34 // Fowler-Warten's method
35 //
36 /* Formula: Description needs graphic, see documentation. */
37 
38 void 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 
80 begin_step: // beginning of step
81 
82  SIMLIB_StepSize = max(SIMLIB_StepSize, SIMLIB_MinStep); // low step limit
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 
90 euler_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 
200  _SetTime(Time,SIMLIB_StepStartTime + SIMLIB_StepSize); // set time to t+h
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  }
226  SIMLIB_StepSize = SIMLIB_OptStep;
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  }
295  SIMLIB_OptStep = max(SIMLIB_OptStep,SIMLIB_MinStep);
296  Dprintf(("Step: %g", SIMLIB_OptStep));
297 
298 } // FW::Integrate
299 
300 
301 ////////////////////////////////////////////////////////////////////////////
302 // FW::PrepareStep
303 // prepare object for integration step
304 //
305 bool FW::PrepareStep(void)
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 //
320 const double FW::prec = DBL_EPSILON; // near zero number
321 
322 }
323 // end of ni_fw.cc
324 
static bool IsEndStepEvent
Definition: simlib.h:1079
int _Print(const char *fmt,...)
output of messages to stdout, too
Definition: print.cc:68
Memory Y1
Definition: ni_fw.h:25
void SIMLIB_Dynamic()
performs evaluation of integrators and status blocks
Definition: continuous.cc:35
double SIMLIB_StepStartTime
last step time
Definition: intg.cc:34
double SIMLIB_ContractStep
requested step size
Definition: intg.cc:59
virtual bool PrepareStep(void) override
prepare object for integration step
Definition: ni_fw.cc:305
const double & OptStep
optimal integration step
Definition: intg.cc:49
Implementation of class CalendarList interface is static - using global functions in SQS namespace...
Definition: algloop.cc:32
double max(double a, double b)
Definition: internal.h:286
double SIMLIB_RelativeError
relative error
Definition: intg.cc:43
int SIMLIB_ERRNO
Definition: intg.cc:32
void SIMLIB_warning(const enum _ErrEnum N)
print warning message and continue
Definition: error.cc:74
const double & Time
model time (is NOT the block)
Definition: run.cc:48
static const double prec
Definition: ni_fw.h:27
double SIMLIB_StepSize
actual step
Definition: intg.cc:40
virtual bool PrepareStep(void)
prepare object for integration step
Definition: numint.cc:282
static bool StateCond(void)
check on changes of state conditions
Definition: numint.cc:175
Memory ERR
Definition: ni_fw.h:25
double min(double a, double b)
Definition: internal.h:285
double SIMLIB_AbsoluteError
absolute error
Definition: intg.cc:42
bool SIMLIB_ContractStepFlag
requests shorter step
Definition: intg.cc:58
Internal header file for SIMLIB/C++.
Fowler-Warten method.
static Iterator LastIntegrator(void)
Definition: simlib.h:1084
virtual void Integrate(void) override
Definition: ni_fw.cc:38
static Iterator FirstIntegrator(void)
Definition: simlib.h:1081
Main SIMLIB/C++ interface.
IntegratorContainer::iterator Iterator
Definition: simlib.h:1080
SIMLIB_IMPLEMENTATION
Definition: algloop.cc:34
#define _SetTime(t, x)
macro for simple assignement to internal time variables
Definition: internal.h:228
bool SIMLIB_ConditionFlag
Definition: cond.cc:30
double SIMLIB_MinStep
minimal step
Definition: intg.cc:38
double SIMLIB_DeltaTime
Time-SIMLIB_StepStartTime.
Definition: intg.cc:35
#define Dprintf(f)
Definition: internal.h:100
double SIMLIB_OptStep
optimal step
Definition: intg.cc:37
bool FW_First
Definition: ni_fw.h:24
double SIMLIB_MaxStep
max. step
Definition: intg.cc:39