///////////////////////////////////////////////////////////////////////////// // ni_euler.cc // // SIMLIB version: 2.18 // Date: 2004-01-25 // // Copyright (c) 1991-2004 Petr Peringer // Copyright (c) 1996-1997 David Leska // // This library is licensed under GNU Library GPL. See the file COPYING. // // // numerical integration: Euler's method // //////////////////////////////////////////////////////////////////////////// // interface // #include "simlib.h" #include "internal.h" #include "ni_euler.h" #include #include //////////////////////////////////////////////////////////////////////////// // implementation // SIMLIB_IMPLEMENTATION //////////////////////////////////////////////////////////////////////////// // Euler's method // /* Formula: y'(t) = f(t, y); y(t+h/2) = y(t) + h/2 * y'(t); y'(t+h/2) = f(t+h/2, y(t+h/2)); y(t+h) = y(t+h/2) + h/2 * y'(t+h/2); y'(t+h) = f(t+h, y(t+h)); err = h * |y'(t) - y'(t+h/2)|; */ void EULER::Integrate(void) { const double err_coef = 0.02; // limits an error range static double dthlf; // half step register size_t i; // auxiliary variables for loops to go through list Iterator ip, end_it; // of integrators static bool DoubleStepFlag; // flag - allow increasing (doubling) the step dprintf((" Euler integration step ")); // print debugging info dprintf((" Time = %g, optimal step = %g", (double)Time, OptStep)); end_it=LastIntegrator(); // end of container of integrators //-------------------------------------------------------------------------- // Step of method //-------------------------------------------------------------------------- begin_step: // beginning of step SIMLIB_StepSize = max(SIMLIB_StepSize, SIMLIB_MinStep); // low step limit dthlf = 0.5*SIMLIB_StepSize; // half step SIMLIB_ContractStepFlag = false; // clear reduce step flag SIMLIB_ContractStep = 0.5*dthlf; // implicitly reduce to half for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) { A[i] = (*ip)->GetOldDiff(); (*ip)->SetState((*ip)->GetOldState() + dthlf*(*ip)->GetDiff()); // state y(t+h/2) } ////////////////////////////////////////////////////////////// 1/2 of step _SetTime(Time, SIMLIB_StepStartTime+dthlf); SIMLIB_DeltaTime = double(Time)-SIMLIB_StepStartTime; SIMLIB_Dynamic(); // compute new state of model (1) if(StateCond()) { // check on changes of state conditions in 1/2 of step goto begin_step; } bool wasContractStepFlag = SIMLIB_ContractStepFlag; // remember value SIMLIB_ContractStepFlag = false; // not reduce step SIMLIB_ContractStep = dthlf; // implicitly reduce to half of step StoreState(di, si, xi); // store values in 1/2 of step for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) { // difference of differentiations for error estimation A[i] -= (*ip)->GetDiff(); (*ip)->SetState(si[i] + dthlf*(*ip)->GetDiff()); } //////////////////////////////////////////////////////////// end of step _SetTime(Time, SIMLIB_StepStartTime + SIMLIB_StepSize); SIMLIB_DeltaTime = SIMLIB_StepSize; SIMLIB_Dynamic(); // compute new state of model (2) //-------------------------------------------------------------------------- // Check on accuracy of numerical integration, estimate error //-------------------------------------------------------------------------- DoubleStepFlag = true; // allow doubling the step SIMLIB_ERRNO = 0; // OK for(ip=FirstIntegrator(),i=0; ip!=end_it; ip++,i++) { double eerr; // estimated error double terr; // greatest allowed error eerr = fabs(SIMLIB_StepSize*A[i]); // error estimation terr = SIMLIB_AbsoluteError + fabs(SIMLIB_RelativeError*si[i]); if(eerr < err_coef*terr) // allowed tolerantion is fulfiled with provision continue; if(eerr > terr) { // allowed tolerantion is overfulfiled if(SIMLIB_StepSize > SIMLIB_MinStep) { // reducing step is possible SIMLIB_OptStep = 0.5*SIMLIB_StepSize; // halve optimal step if(SIMLIB_OptStep < SIMLIB_MinStep) { // limit of optimal step SIMLIB_OptStep = SIMLIB_MinStep; } SIMLIB_StepSize = SIMLIB_OptStep; IsEndStepEvent = false; goto begin_step; // compute again with smaller step } // reducing step is unpossible SIMLIB_ERRNO++; // requested accuracy cannot be achieved _Print("\n Integrator[%lu] ",(unsigned long)i); if(SIMLIB_ConditionFlag) // event has been within the step break; } DoubleStepFlag = false; // disable increasing SIMLIB_OptStep, // accuracy is sufficient, but not well } // for if(SIMLIB_ERRNO) { SIMLIB_warning(AccuracyError); } //-------------------------------------------------------------------------- // Computation is continuing, compute y(t+h) //-------------------------------------------------------------------------- if(wasContractStepFlag) { // step reducing has been requested in half step and it is unpossible RestoreState(dthlf, di, si, xi); // restore halfstep state } else { // go to half step and complete the computation GoToState(di, si, xi); SIMLIB_StepStartTime += dthlf; SIMLIB_DeltaTime = double(Time) - SIMLIB_StepStartTime; //----------------------------------------------------------------------- // Analyse system at the end of the step //----------------------------------------------------------------------- if(StateCond()) { // check on changes of state conditions at end of step goto begin_step; } } //-------------------------------------------------------------------------- // Results of step have been accepted, take fresh step //-------------------------------------------------------------------------- // increase step, if accuracy was good // step increasing is allowed // && method is not used to start multi-step method if(DoubleStepFlag && !IsStartMode()) { SIMLIB_OptStep += SIMLIB_OptStep; // step doubling } SIMLIB_OptStep = min(SIMLIB_OptStep,SIMLIB_MaxStep); // limit step size } // EULER::Integrate //////////////////////////////////////////////////////////////////////////// // end of file ni_euler.cpp ////////////////////////////////////////////////////////////////////////////