///////////////////////////////////////////////////////////////////////////// // numint.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 -- base file // //////////////////////////////////////////////////////////////////////////// // interface // #include "simlib.h" #include "internal.h" #include "ni_abm4.h" #include "ni_euler.h" #include "ni_fw.h" #include "ni_rke.h" #include "ni_rkf3.h" #include "ni_rkf5.h" #include "ni_rkf8.h" #include #include //////////////////////////////////////////////////////////////////////////// // implementation // SIMLIB_IMPLEMENTATION // stack options in Borland C++ compiler #ifdef __BORLANDC__ // increase stack size // WARNING: under MS-Windows must be specified in *.DEF file !!! ### // extern unsigned _stklen = 0xA000; // set option 'check stack owerflow' on # pragma option -N #endif //////////////////////////////////////////////////////////////////////////// // outline methods of class IntegrationMethod //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::StepSim // step of numerical integration method // void IntegrationMethod::StepSim(void) { dprintf(("==================== continuous step BEGIN %.15g",Time)); #ifndef NDEBUG double Step_StartTime = Time; #endif SIMLIB_DynamicFlag = true; // numerical integration is running if(Prepare()) { // initialize integration step (condition is not changed) if(IntegratorContainer::isAny()) { // are there any integrators? CurrentMethodPtr->Integrate(); // * numerical integration * } else { // model without integrators Iterate(); // compute new values of state blocks } Summarize(); // set up new state in the system } SIMLIB_DynamicFlag = false; // end of numerical integration dprintf((" Step length = %g ", Time - Step_StartTime )); dprintf(("==================== continuous step END %.15g",Time)); } // IntegrationMethod::StepSim //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::SetMethod // set method which will be used // void IntegrationMethod::SetMethod(const char* name) { dprintf(("SetMethod(%s, %s)", name)); if(SIMLIB_DynamicFlag) { SIMLIB_error(NI_CantSetMethod); // can't in 'dynamic section' !!! } CurrentMethodPtr->TurnOff(); // suspend present method CurrentMethodPtr=SearchMethod(name); // set new method } // IntegrationMethod::SetMethod //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::Iterate // compute new values of state blocks in model without integrators // void IntegrationMethod::Iterate(void) { dprintf(("IntegrationMethod::Iterate()")); while(1) { SIMLIB_StepSize = max(SIMLIB_MinStep, SIMLIB_StepSize); SIMLIB_ContractStepFlag = false; // don't reduce step SIMLIB_ContractStep = 0.5*SIMLIB_StepSize; // implicitly reduce to half _SetTime(Time, SIMLIB_StepStartTime + SIMLIB_StepSize); SIMLIB_DeltaTime = SIMLIB_StepSize; SIMLIB_Dynamic(); // evaluate new state of model - only state blocks Condition::TestAll(); // check on changes of state conditions if(!SIMLIB_ContractStepFlag) break; if(SIMLIB_StepSize<=SIMLIB_MinStep) break; IsEndStepEvent = false; // no event will be at end of step SIMLIB_StepSize = SIMLIB_ContractStep; StatusContainer::LtoN(); } } // IntegrationMethod::Iterate //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::Summarize // set up new state after execution of integration step // void IntegrationMethod::Summarize(void) { dprintf(("IntegrationMethod::Summarize()")); SIMLIB_StepStartTime = Time; SIMLIB_DeltaTime = 0.0; IntegratorContainer::NtoL(); StatusContainer::NtoL(); if(IsEndStepEvent) // event at the end of step _SetTime(Time, NextTime); // suppress inaccuracy of float } // IntegrationMethod::Summarize //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::Prepare -- initialize integration step // bool IntegrationMethod::Prepare(void) { SIMLIB_StepSize = SIMLIB_OptStep; // optimal step size at start dprintf(("IntegrationMethod::Prepare()")); // If an event is scheduled within the step, // set on flag, that will be event at the end of the step IsEndStepEvent=(bool)(double(Time)+1.01*SIMLIB_StepSize>=NextTime);//1.1??? // and adjust step size, so that event will take place at end of step if(IsEndStepEvent) SIMLIB_StepSize = double(NextTime)-double(Time); // set up auxiliary variables SIMLIB_StepStartTime = Time; // start time of integration SIMLIB_DeltaTime = 0.0; // time since beginning of integration if(SIMLIB_ResetStatus) { // initialization of integration is requested // eg. on event - non-continuous change of state // --------------- initialization of step sequence ---------------- IntegratorContainer::NtoL();// store initial value ... StatusContainer::NtoL(); SIMLIB_Dynamic(); // compute new (initial) state (0) Condition::TestAll(); // check on changes of state conditions IntegratorContainer::NtoL();// store new value ... StatusContainer::NtoL(); SIMLIB_ResetStatus=false; // clear reset flag if(SIMLIB_ConditionFlag) return false; // condition has been changed => terminate step } if(SIMLIB_StepSize<=0) SIMLIB_error(NI_IlStepSize); // error of integration CurrentMethodPtr->PrepareStep(); // prepare current method for single step return true; } // IntegrationMethod::Prepare //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::StateCond // check on changes of state conditions // bool IntegrationMethod::StateCond(void) { dprintf(("IntegrationMethod::StateCond()")); Condition::TestAll(); // check on changes if(SIMLIB_ContractStepFlag && SIMLIB_StepSize>SIMLIB_MinStep) { // step reducing is requested and it is possible SIMLIB_StepSize = SIMLIB_ContractStep; // reduce step to demanded size // implicitly to quater of step IsEndStepEvent = false; // no event will be scheduled at end of step return true; } return false; } // IntegrationMethod::StateCond //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::IntegrationMethod // Constructor registrates method and names it // IntegrationMethod::IntegrationMethod(const char* name) : PrevINum(0) { dprintf(("constructor[IntegrationMethod]: \"%s\"(%p)", name, MthLstPtr)); // name the method Name=new char[strlen(name)+1]; strcpy(Name,name); // is list of methods not created? if(MthLstPtr==NULL) { MthLstPtr=new dlist; // create it } // registrate the method for(ItList=MthLstPtr->begin(); ItList!=MthLstPtr->end(); ItList++) { if(strcmp((*ItList)->Name,Name)==0) { // method name must be unique SIMLIB_error(NI_MultDefMeth); // der Fehler } } ItList=MthLstPtr->insert(MthLstPtr->end(),this); // insert method into list PtrMList=&MList; // initialize pointer to list of memories } // IntegrationMethod::IntegrationMethod //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::~IntegrationMethod // Destructor unregistrates method // IntegrationMethod::~IntegrationMethod() { dprintf(("destructor[IntegrationMethod]")); // is list of methods not created? if(MthLstPtr==NULL) { SIMLIB_internal_error(); // internal error (probably impossible) } MthLstPtr->erase(ItList); // unregistrate the method delete[] Name; // free dynamic data if(MthLstPtr->empty()) { // destroy the list delete MthLstPtr; MthLstPtr=NULL; } } // IntegrationMethod::~IntegrationMethod //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::SearchMethod // search method in the list of registrated methods // IntegrationMethod* IntegrationMethod::SearchMethod(const char* name) { dlist::iterator it; // iterator for searching in the list dlist::iterator end_it; // iterator to end of the list dprintf(("IntegrationMethod::SearchMethod(\"%s\")", name)); // is list of methods created? if(MthLstPtr!=NULL) { // search for method in the list of registrated methods for(it=MthLstPtr->begin(), end_it=MthLstPtr->end(); it!=end_it; it++) { if(strcmp((*it)->Name,name)==0) { // find? return *it; // return pointer to the method } } } SIMLIB_error(NI_UnknownMeth); // name is unknown (or list is not created) return 0; // never reached } // IntegrationMethod::SearchMethod //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::TurnOff // turn off integration method: flush memories etc... // void IntegrationMethod::TurnOff(void) { dprintf(("IntegrationMethod::TurnOff()")); Resize(0); // flush memories PrevINum=0; // remember it for next usage } // IntegrationMethod::TurnOff //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::Resize // resize all the memories to the given size // void IntegrationMethod::Resize(size_t size) { dprintf(("IntegrationMethod::Resize(%lu)", (long unsigned)size)); for(dlist::iterator it=MList.begin(); it!=MList.end(); it++) { (*it)->Resize(size); } } // IntegrationMethod::Resize //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::PrepareStep // prepare object for integration step // bool IntegrationMethod::PrepareStep(void) { dprintf(("IntegrationMethod::PrepareStep()")); // has # of integrators been changed? if(PrevINum!=IntegratorContainer::Size()) { PrevINum=IntegratorContainer::Size(); // retain # of integrators Resize(PrevINum); // change size of auxiliary memories return true; // there are changes in system } else { return false; // no changes } } // IntegrationMethod::PrepareStep /* auxiliary functions for user to add own method */ //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::InitStep // initialize step // void IntegrationMethod::InitStep(double step_frag) { SIMLIB_StepSize = max(SIMLIB_StepSize, SIMLIB_MinStep); // low step limit SIMLIB_StepSize = min(SIMLIB_StepSize, SIMLIB_MaxStep); // high step limit SIMLIB_ContractStepFlag = false; // clear reduce step flag // implicitly reduce to half step SIMLIB_ContractStep = step_frag*SIMLIB_StepSize; } /* InitStep */ //////////////////////////////////////////////////////////////////////////// // IntegrationMethod::FunCall // evaluate y'(t) = f(t, y(t)) // void IntegrationMethod::FunCall(double step_frag) { if(step_frag==1.0) { // accuracy! // substep time _SetTime(Time, SIMLIB_StepStartTime + SIMLIB_StepSize); SIMLIB_DeltaTime = SIMLIB_StepSize; } else { // substep time _SetTime(Time, SIMLIB_StepStartTime + step_frag*SIMLIB_StepSize); SIMLIB_DeltaTime = double(Time) - SIMLIB_StepStartTime; } SIMLIB_Dynamic(); // evaluate new state of model } /* FunCall */ //////////////////////////////////////////////////////////////////////////// // outline methods of class IntegrationMethod::Memory //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Memory::Resize // change size of array to cs, content will be undefined! // void IntegrationMethod::Memory::Resize(size_t cs) { dprintf(("IntegrationMethod::Memory::Resize(%lu)",(long unsigned)cs)); if(cs == 0) { // zero size delete[] arr; arr = NULL; mem_size = 0; } else { cs = (1+(cs-1)/page_size)*page_size; // round new size to page size if(cs != mem_size) { // demanded size is too small or too large delete[] arr; // new allocation arr = new double[cs]; if(arr==NULL) { // cannot allocate memory SIMLIB_internal_error(); // there is an exception in new versions of C++ } #ifdef __BCPLUSPLUS__ // problems with FU***NG MeS-DOS segment memory model! if(cs > 0xFFFF/sizeof(double) ) SIMLIB_internal_error(); #endif mem_size = cs; dprintf(("##### reallocation to mem_size=%lu",(long unsigned)mem_size)); } } } // Memory::Resize //////////////////////////////////////////////////////////////////////////// // Memory::Memory // create empty memory, put object into list // IntegrationMethod::Memory::Memory(dlist* PtrList) : arr(NULL), mem_size(0), ListPtr(PtrList) { // dprintf(("constructor: IntegrationMethod::Memory::Memory(%p)", PtrList)); it_list=ListPtr->insert(ListPtr->end(),this); // put memory into list } //////////////////////////////////////////////////////////////////////////// // Memory::~Memory // free dynamic data, remove object from list of memories // IntegrationMethod::Memory::~Memory() { // dprintf(("destructor: IntegrationMethod::Memory::~Memory()")); delete[] arr; // free allocated memory arr=NULL; mem_size=0; ListPtr->erase(it_list); // remove object from list } //////////////////////////////////////////////////////////////////////////// // outline methods of class MultiStepMethod //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::MultiStepMethod // Constructor initializes method // MultiStepMethod::MultiStepMethod(const char* name, const char* slave_name) : IntegrationMethod(name), Slave_Ptr(NULL) { dprintf(("constructor[MultiStepMethod](%s, %s)", name, slave_name)); // name the starting method SlaveName=new char[strlen(slave_name)+1]; strcpy(SlaveName, slave_name); // note: cannot initialize Slave_Ptr here, // slave need not exist now because it is a global object // you must determine it by a name } // MultiStepMethod::MultiStepMethod //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::~MultiStepMethod // Destructor, it frees dynamic data // MultiStepMethod::~MultiStepMethod() { dprintf(("destructor[MultiStepMethod]")); delete[] SlaveName; // free dynamic data } // MultiStepMethod::~MultiStepMethod //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::SlavePtr // return pointer to the starting method (initialize it also) // SingleStepMethod* MultiStepMethod::SlavePtr(void) { if(Slave_Ptr==NULL) { // pointer is not valid Slave_Ptr=(SingleStepMethod*)SearchMethod(SlaveName); // obtain method if(!Slave_Ptr->IsSingleStep()) { // Is it a single-step method? SIMLIB_error(NI_NotSingleStep); // no -- error } } return Slave_Ptr; } // MultiStepMethod::SlavePtr //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::PrepareStep // prepare the object for the step of integration // bool MultiStepMethod::PrepareStep(void) { bool ichanges; dprintf(("MultiStepMethod::PrepareStep()")); // prepare inherited part & test if # of integrators has been changed ichanges = IntegrationMethod::PrepareStep(); SlavePtr()->SetStartMode(true); // called method is used for starting (void)SlavePtr()->PrepareStep(); // prepare your slave return ichanges; // indicate changes } // MultiStepMethod::PrepareStep //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::TurnOff // turn off integration method and its slave (starter) // void MultiStepMethod::TurnOff(void) { dprintf(("MultiStepMethod::TurnOff()")); IntegrationMethod::TurnOff(); // turn off inherited part SlavePtr()->SetStartMode(false); // free slave SlavePtr()->TurnOff(); // turn off your slave } // MultiStepMethod::TurnOff //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::SetStarter // set starting method for given multi-step method // void MultiStepMethod::SetStarter(const char* name, const char* slave_name) { dprintf(("SetStarter(%s, %s)", name, slave_name)); MultiStepMethod* ptr=(MultiStepMethod*)SearchMethod(name); if(ptr->IsSingleStep()) { // is current method single-step? SIMLIB_error(NI_NotMultiStep); // yes -- error } ptr->SetStarter(slave_name); // set it } // MultiStepMethod::SetStarter //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::SetStarter // set starting method for the multi-step method // void MultiStepMethod::SetStarter(const char* slave_name) { dprintf(("SetStarter(%s)", slave_name)); if(SIMLIB_DynamicFlag) { SIMLIB_error(NI_CantSetStarter); // can't in 'dynamic section' !!! } // name the starting method delete[] SlaveName; SlaveName=new char[strlen(slave_name)+1]; strcpy(SlaveName, slave_name); Slave_Ptr=NULL; // throw away old starting method } // MultiStepMethod::SetStarter //////////////////////////////////////////////////////////////////////////// // MultiStepMethod::GetStarter // obtain the name of the starting method of given method // char* const MultiStepMethod::GetStarter(const char* name) { dprintf(("GetStarter(%s)", name)); MultiStepMethod* ptr=(MultiStepMethod*)SearchMethod(name); if(ptr->IsSingleStep()) { // is the method single-step? return NULL; // no starter is used } return ptr->SlaveName; // get it } // MultiStepMethod::GetStarter //////////////////////////////////////////////////////////////////////////// // outline methods of class StatusIntegrationMethod //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // StatusMethod::StatusMethod -- initailization // StatusMethod::StatusMethod(const char* name): SingleStepMethod(name), PrevStatusNum(0) { dprintf(("constructor[StatusIntegrationMethod]: \"%s\"", name)); PtrStatusMList=&StatusMList; // initialize } // StatusMethod //////////////////////////////////////////////////////////////////////////// // StatusMethod::TurnOff // turn off integration method: flush memories etc... // void StatusMethod::TurnOff(void) { dprintf(("StatusMethod::TurnOff()")); IntegrationMethod::TurnOff(); // turn off inherited part StatusResize(0); // flush status memories PrevStatusNum=0; // remember it for next usage } // TurnOff //////////////////////////////////////////////////////////////////////////// // StatusMethod::PrepareStep // prepare object for integration step // bool StatusMethod::PrepareStep(void) { dprintf(("StatusMethod::PrepareStep()")); // prepare inherited part -- test for changes of # of integrators bool ichanges = IntegrationMethod::PrepareStep(); // has # of status variables been changed? if(PrevStatusNum!=StatusContainer::Size()) { PrevStatusNum=StatusContainer::Size(); // retain # of status variables StatusResize(PrevStatusNum); // change size of status auxiliary memories return true; // there are changes in system } else { return ichanges; // changes would be only in iherited part } } // PrepareStep //////////////////////////////////////////////////////////////////////////// // StatusMethod::StatusResize // resize status memories to the given size // void StatusMethod::StatusResize(size_t size) { dprintf(("StatusMethod::StatusResize(%lu)", (long unsigned)size)); for(dlist::iterator it=StatusMList.begin(); it!=StatusMList.end(); it++) { (*it)->Resize(size); } } // StatusResize //////////////////////////////////////////////////////////////////////////// // StatusMethod::StoreState // store state of integrators and status variables // void StatusMethod::StoreState(Memory& di, Memory& si, StatusMemory& xi) { register size_t i; IntegratorContainer::iterator ip, end_it; StatusContainer::iterator sp, status_end_it; for(ip=IntegratorContainer::Begin(), end_it=IntegratorContainer::End(), i=0; ip!=end_it; ip++, i++) { di[i]=(*ip)->GetDiff(); si[i]=(*ip)->GetState(); } for(sp=StatusContainer::Begin(), status_end_it=StatusContainer::End(), i=0; sp!=status_end_it; sp++, i++) { xi[i]=(*sp)->GetState(); } } // StoreState //////////////////////////////////////////////////////////////////////////// // StatusMethod::RestoreHalfState // restore state of integrators and status variables // void StatusMethod::RestoreState(double dthlf, Memory& di, Memory& si, StatusMemory& xi) { register size_t i; IntegratorContainer::iterator ip, end_it; StatusContainer::iterator sp, status_end_it; for(ip=IntegratorContainer::Begin(), end_it=IntegratorContainer::End(), i=0; ip!=end_it; ip++, i++) { (*ip)->SetDiff(di[i]); (*ip)->SetState(si[i]); } for(sp=StatusContainer::Begin(), status_end_it=StatusContainer::End(), i=0; sp!=status_end_it; sp++, i++) { (*sp)->SetState(xi[i]); } _SetTime(Time, SIMLIB_StepStartTime + dthlf); // half step IsEndStepEvent = false; // no event will be scheduled at end of step } // RestoreState //////////////////////////////////////////////////////////////////////////// // StatusMethod::GoToState // move startpoint to given state // void StatusMethod::GoToState(Memory& di, Memory& si, StatusMemory& xi) { register size_t i; IntegratorContainer::iterator ip, end_it; StatusContainer::iterator sp, status_end_it; for(ip=IntegratorContainer::Begin(), end_it=IntegratorContainer::End(), i=0; ip!=end_it; ip++, i++) { (*ip)->SetOldDiff(di[i]); (*ip)->SetOldState(si[i]); } for(sp=StatusContainer::Begin(), status_end_it=StatusContainer::End(), i=0; sp!=status_end_it; sp++, i++) { (*sp)->SetOldState(xi[i]); } } // GoToState //////////////////////////////////////////////////////////////////////////// // initialize static members of classes // // size of memory page const size_t IntegrationMethod::Memory::page_size = 256; // flag - will be event at the end of the step? bool IntegrationMethod::IsEndStepEvent=false; // list of registrated methods dlist* IntegrationMethod::MthLstPtr=NULL; // pointer to the filled list of memories dlist* IntegrationMethod::PtrMList; // pointer to the filled list of status memories dlist* StatusMethod::PtrStatusMList; //////////////////////////////////////////////////////////////////////////// // constitute integration methods // ABM4 abm4("abm4", "rkf5"); EULER euler("euler"); FW fw("fw"); RKE rke("rke"); RKF3 rkf3("rkf3"); RKF5 rkf5("rkf5"); RKF8 rkf8("rkf8"); // pointer to the method used at present // rke is a predefined method IntegrationMethod* IntegrationMethod::CurrentMethodPtr = &rke; // end