Inherited by FreeFall, Oscillator, RealBounce, and RealFall.
Continuous supports Trace.
//---------------------------------------------------------------------------- // Copyright (C) 2002, 2003 Humboldt-Universitaet zu Berlin // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //---------------------------------------------------------------------------- #include <odemx/base/Continuous.h> #include <cmath> using namespace odemx; // // The first Continuous process is very simple. It only defines // a sinus/co-sinus oscillator. // class Oscillator : public Continuous { public: // // The construction of a Continuous object is a little different to // that of a Process object. You also have to provide the number // of state-variables used by your process. In case of Oscillator // we need two variables. // Oscillator() : Continuous(getDefaultSimulation(), "Oscillator", 2) {}; protected: // // The main-function of a continuous process is quite similar to that of // a discrete process. You can do everything that is possible in Process. // That's why a continuous can behave just like a discrete process. But // it can also go through phases of continuous state changes. // virtual int main() { // // Before you can start the solver, which is computing the continuous // state changes, you will have to initialise the state variables. // state[0]=1; state[1]=0; // // Than you should set some parameters for the internal solver. These // parameters include error sensitivity and the step length. // // The step length is set with 'setStepLength()'. The first parameter // sets the minimal step length, while the second defines the maximal // step length. The internal solver will compute new states in steps. // Between every step the process holds. The actual length of the step // taken will depend on numerical errors, peer processes, state events, // and the parameters provided with 'setStepLength()'. The actual step // length will not exceed your provided maximum. If the step length has // to be reduced because of numerical errors or state events it will not // be reduced below your provided minimum. // setStepLength(0.01, 0.1); // // The error sensitivity is set with 'setErrorlimit()'. The first parameter // defines whether the errors should be considered relative to the value of // the state variables (0) or absolute (1). The second parameter // sets the maximum error acceptable (relative or absolute). If the actual // error exceeds this value the solver will try to reduce the step length. // If this fails, because the step length is already to small, you will get // a simulation error. // setErrorlimit(0, 0.1); // // Finally, the continuous phase is started with 'integrate()'. It is stopped // either by a time event, a state event or an interrupt from another process. // // The time event is set by the first parameter. If it is 0 the solver will // run for ever. Otherwise it will run to the given absolute time. If the // provided time has already passed it will return at once. The return value of // integrate will be 0 if the time event was hit. If the process is interrupted // 'integrate()' returns 2. // integrate(20.0, 0); return 0; } // // Every Continuous process has to provide its specific 'derivatives()' function. // In this function you define how the state changes during the time. You do this // by setting the rate in which a state variable is changed. Although you don't have // to, you can include the time provided by t in your computation. But never use // 'getCurrentTime()' to get the time. // virtual void derivatives (double t) { rate[0]=-state[1]; rate[1]=state[0]; } }; // // The FreeFall continuous process needs only one state variable. It demonstrates // the use of parameter t in 'derivatives()'. The result is an idealistic free fall. // class FreeFall : public Continuous { public: FreeFall() : Continuous(getDefaultSimulation(), "FreeFall", 1) {}; protected: virtual int main() { state[0]=0.0; setStepLength(.1,1); integrate(20.0, 0); return 0; } // // Again, never use getCurrentTime() to include the current time in your // computation. The reason for this is, that 'derivatives()' is called multiple // times for each step and these calls are not synchronised to the // 'official' time in the simulation. // virtual void derivatives (double t) { double g=9.81; rate[0]=t*g; } }; // // RealFall simulates a 'real fall' which is slowed down by friction. // class RealFall : public Continuous { public: RealFall() : Continuous(getDefaultSimulation(), "RealFall", 2) {}; protected: // // As in FreeFall we don't set the error limits. We can do so because // ODEMx uses default settings for error limits and step length. The // default for the error limits is a relative (0) error limit of 0.1 . // virtual int main() { state[0]=2.0; state[1]=0.0; setStepLength(.1, 1); integrate(20.0, 0); return 0; } virtual void derivatives (double t) { double k=0.5; double g=-9.81; rate[0]=state[1]; rate[1]=g - k*state[1]; } }; // // RealBounce finally demonstrates the use of state events. // class RealBounce : public Continuous { public: RealBounce() : Continuous(getDefaultSimulation(), "RealBounce", 3) {}; // // 'hitGround()' is used to check a state event. The signature // of functions that can be used as state-event-functions is: // bool(Process::*)() // ODEMx uses pointer to member functions if it needs a call-back. // The advantage is, you can use member functions with full access // to internal data of your classes to check state events. The // costs of this design decision is that you will always have to // cast the address of your state-event-function to the type 'Condition'. // // A state function has to return true if a state event has occurred. // Remember, the computation of state changes is done in steps. Because // of that it is unlikely to hit a state event exactly. This has to // be considered when programming a state-event-function. If a state // event has occurred (or passed) the internal solver starts a binary // search to get closer to the exact event time. Finally, if it gets close // enough (minimum step length) the computation is stopped and 'integrate()' // returns 1. // bool hitGround() { return state[0]<=0.0; } protected: virtual int main() { double g=-9.81; state[0]=2.0; state[1]=0.0; state[2]=g; setStepLength(0.01, 0.1); // // RealBounce uses the return value of 'integrate()' to control // the computation. Remember 'integrate()' returns 0 if a // time event occurred, 2 if the process was interrupted and 1 // if a state event was detected. // 'integrate()' is called with the time event 20.0 and the // state-event-function 'hitGround()'. If the state event // is hit we reflect the movement 'state[1]=-(0.8*state[1])' // and continue until the simulation time exceeds 20.0. // while (integrate(20.0, (Condition)&RealBounce::hitGround)==1) { // // The ball hit the ground and is reflected. // if (fabs(state[1])<0.01 && state[0]<0.01) { // // We must stop the ball if it has lost to much energy. // Otherwise we would produce an annoying loop. The // state event would be hit in every step. // state[1]=0.0; state[2]=0.0; } state[1]=-(0.8*state[1]); } return 0; } virtual void derivatives (double t) { double k=0.5; rate[0]=state[1]; rate[1]=state[2] - k*fabs(state[1]); rate[2]=0.0; } }; int main(int argc, char* argv[]) { Oscillator osci; FreeFall free; RealFall real; RealBounce bounce; // // To follow the state changes we use the class ContuTrace. // ContuTrace observes a provided continuous process and logs all // state changes into a text file. The file is managed by ContuTrace. // The name is either set in the constructor or build from the // name of the observed continuous process. If you run the simulation // you will find the 4 text files: // Oscillator_trace.txt // FreeFall_trace.txt // RealFall_trace.txt // RealBounce_trace.txt // ContuTrace tracer[] = {ContuTrace(&osci), ContuTrace(&free), ContuTrace(&real), ContuTrace(&bounce)}; // // Continuous processes are activated just like discrete processes. // osci.activate(); free.activate(); real.activate(); bounce.activate(); // // We can run the simulation without a time limit because all // our continuous processes end at 20.0 . // getDefaultSimulation()->run(); return 0; }
Public Member Functions | |
Continuous (Simulation *s, Label l, int d, ContinuousObserver *o=0) | |
Construction. | |
virtual | ~Continuous () |
Destruction. | |
virtual void | interrupt () |
Interrupt. | |
unsigned int | getDimension () const |
Get number of state variables. | |
void | setStepLength (double min, double max) |
Set minimum and maximum step length. | |
double | getStepLength () const |
Get current step length. | |
void | setErrorlimit (int n, double d) |
Set errorlimit. | |
void | addPeer (Process *p) |
Add a partner process. | |
void | delPeer (Process *p) |
Remove a peer. | |
Data Fields | |
std::vector< double > | state |
State variables. | |
std::vector< double > | rate |
Rate variables. | |
std::vector< double > | error_vector |
Error vector. | |
double | error_value |
Error. | |
Static Public Attributes | |
Trace MarkTypes | |
These MarkTypes are used to trace Continuous events. A TraceConsumer can use these constants to identify trace events send by Continuous. | |
const MarkTypeId | baseMarkId = 1000 |
const MarkType | markCreate |
const MarkType | markDestroy |
const MarkType | markAddPeer |
const MarkType | markRemovePeer |
const MarkType | markBeginIntegrate |
const MarkType | markEndIntegrate |
const MarkType | markNewValidState |
const TagId | baseTagId = 1000 |
Protected Member Functions | |
virtual int | main ()=0 |
User defined process behaviour. | |
virtual void | derivatives (double t)=0 |
User defined continuous behaviour. | |
int | integrate (SimTime timeEvent, Condition stateEvent=0) |
Computation of time continuous state changes. | |
virtual double | errorNorm () |
Compute error. | |
int | reduce () |
void | binSearch () |
virtual void | takeAStep (double h) |
bool | stopIntegrate () |
Protected Attributes | |
int | dimension |
double | stepLength |
double | maxStepLength |
double | minStepLength |
double | time |
int | relative |
double | errorLimit |
int | stoped |
std::vector< double > | initial_state |
std::vector< double > | slope_1 |
std::vector< double > | slope_2 |
std::vector< double > | slope_3 |
SimTime | stopTime |
Condition | stopCond |
std::list< Process * > | peers |
|
Construction.
|
|
Interrupt. If a Continuous process is interrupted inside integrate() the computation is not synchronised to the time of the interrupt. There might be an error depending on the step length. This can be prevented if the process that caused the interrupt is registered as a peer to the continuous process.
Reimplemented from Process. |
|
Get number of state variables. 5getDimension() returns the number of state variables specified in the constructor. No more than the specified state variables may me used in derivatives(). The state variables are stored in the STL vector state. |
|
Set minimum and maximum step length.
|
|
Get current step length. The integrate() function computes state changes step by step. The step length used is variable depending on numerical errors, state events and registered peer processes. getStepLength() returns the current step length.
|
|
Set errorlimit.
The limit can be set absolute or relative. In the second case the limit is interpreted relative to the actual state values.
|
|
Add a partner process.
|
|
Remove a peer.
|
|
User defined process behaviour. This function is implemented by the user to define the process behaviour.
Implements Process. |
|
User defined continuous behaviour.
|
|
Computation of time continuous state changes.
timeEvent is reached, the state event occurred or the interrupt() function was called. The computation is done step by step with a varying step length. The actual step length depends on computation errors, registered peers and the stop time ( timeEvent ). The user can set boundaries to the step length with the function setStepLength(). The default values used by integrate are 0.01 and 0.1 for minimum and maximum. The step by step computation of time continuous processes introduces an inherent error. integrate() observes the computational errors for each state variable. The function errorNorm() is called to compute a single value from all error values. The user can use the function setErrorlimit() to specify the largest acceptable error. If the single value computed by errorNorm() exceeds the acceptable error integrate() tries to reduce the step length. This fails if the resulting step length would be smaller than the minimum step length set with setStepLength(). The default error limit is an absolute error of 0.1. If during the computation a state event occurred, integrate() starts a binary search to find the exact time of the event. The minimum step length set by setStepLength() defines the accuracy of this search. integrate() synchronises computation with partner processes. A process is registered as a partner with the function addPeer().
|
|
Compute error.
|
|
State variables.
The
|
|
Rate variables.
The STL vector
|
|
Error vector.
The step by step computation of time continuous state changes introduces an inherent error. This error is computed for every state variable independently.
|
|
Error.
The step by step computation of time continuous state changes introduces an inherent error. This error is computed for every state variable independently. The function errorNorm() is used to compute a single error value from the vector of error values which is stored in
|