NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
odesolver.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2015 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "odesolver.hpp"
20#include "../../kernel.hpp"
21
22using namespace std;
23
25
29
31{
32 _odeParser = 0;
33 _odeData = 0;
34 _odeFunctions = 0;
35 _odeSettings = 0;
36
37 odeStepType = 0;
38 odeStep = 0;
39 odeControl = 0;
40 odeEvolve = 0;
41
42 nDimensions = 0;
43}
44
46{
47 _odeParser = _parser;
48 _odeData = _data;
49 _odeFunctions = _functions;
50 _odeSettings = _option;
51}
52
54{
55 _odeParser = 0;
56 _odeData = 0;
57 _odeFunctions = 0;
58 _odeSettings = 0;
59
60 //cerr << odeEvolve << endl;
61 //cerr << odeControl << endl;
62 //cerr << odeStep << endl;
63
64 if (odeEvolve)
65 gsl_odeiv_evolve_free(odeEvolve);
66 if (odeControl)
67 gsl_odeiv_control_free(odeControl);
68 if (odeStep)
69 gsl_odeiv_step_free(odeStep);
70}
71
72
73int Odesolver::odeFunction(double x, const double y[], double dydx[], void* params)
74{
75 int nResults = 0;
76 mu::value_type* v = 0;
77
78 // Variablen zuweisen
79 _defVars.vValue[0][0] = x;
80
81 for (int i = 0; i < nDimensions; i++)
82 {
83 *(mVars.find("y"+toString(i+1))->second) = y[i];
84 }
85
86 v = _odeParser->Eval(nResults);
87 for (int i = 0; i < nResults; i++)
88 {
89 dydx[i] = v[i].real();
90 }
91 return GSL_SUCCESS;
92}
93
94bool Odesolver::solve(const string& sCmd)
95{
97 return false;
98
99 // Warum auch immer diese Pointer an dieser Stelle bereits eine Adresse hatten...???
100 //odeEvolve = 0;
101 //odeControl = 0;
102 //odeStep = 0;
103
104 const gsl_odeiv_step_type* odeStepType_ly = 0;
105 gsl_odeiv_step* odeStep_ly = 0;
106 gsl_odeiv_control* odeControl_ly = 0;
107 gsl_odeiv_evolve* odeEvolve_ly = 0;
108 gsl_odeiv_system odeSystem_ly;
109
110
111 //cerr << 1 << endl;
112 double t0 = 0.0;
113 double t1 = 0.0;
114 double t2 = 0.0;
115 double dt = 0.0;
116 double h = 0.0;
117 double h2 = 0.0;
118 double dRelTolerance = 0.0;
119 double dAbsTolerance = 0.0;
120 int nSamples = 100;
121 int nLyapuSamples = 100;
122 mu::value_type* v = 0;
123 vector<double> vInterval;
124 vector<double> vStartValues;
125
126 string sFunc = "";
127 string sParams = "";
128 string sTarget = "ode()";
129 string sVarDecl = "y1";
130 Indices _idx;
131 bool bAllowCacheClearance = false;
132 bool bCalcLyapunov = false;
133
134 time_t tTimeControl = time(0);
135
136 double* y = 0;
137 double* y2 = 0;
138 double lyapu[2] = {0.0, 0.0};
139 double dist[2] = {1.0e-6,0.0};
140 double t = 0.0;
141
142 if (NumeReKernel::getInstance()->getStringParser().isStringExpression(sCmd))
143 {
144 //sErrorToken = "odesolve";
146 }
147
148 if (sCmd.find("-set") != string::npos || sCmd.find("--") != string::npos)
149 {
150 if (sCmd.find("-set") != string::npos)
151 {
152 sFunc = sCmd.substr(0,sCmd.find("-set"));
153 sParams = sCmd.substr(sCmd.find("-set"));
154 }
155 else
156 {
157 sFunc = sCmd.substr(0,sCmd.find("--"));
158 sParams = sCmd.substr(sCmd.find("--"));
159 }
160 sFunc.erase(0,findCommand(sFunc).nPos+8); //odesolve EXPR -set...
161 StripSpaces(sFunc);
162 }
163 else
165
166 //cerr << 2 << endl;
167 if (!sFunc.length())
169 if (!_odeFunctions->call(sFunc))
170 throw SyntaxError(SyntaxError::FUNCTION_ERROR, sCmd, sFunc, sFunc);
173
174 if (findParameter(sParams, "target", '='))
175 {
176 sTarget = getArgAtPos(sParams, findParameter(sParams, "target", '=')+6);
177 sParams.erase(sParams.find(sTarget, findParameter(sParams, "target", '=')+6), sTarget.length());
178 sParams.erase(findParameter(sParams, "target", '=')-1, 7);
179 if (sTarget.find('(') == string::npos)
180 {
181 sTarget += "()";
182 bAllowCacheClearance = true;
183 }
184 }
185 else
186 bAllowCacheClearance = true;
187
188 //cerr << 3 << endl;
189 if (!_odeData->isTable(sTarget))
190 _odeData->addTable(sTarget, *_odeSettings);
191
192 getIndices(sTarget, _idx, *_odeParser, *_odeData, *_odeSettings);
193
194 if (!isValidIndexSet(_idx))
195 return false;
196
197 sTarget.erase(sTarget.find('('));
198
199 if (!_odeFunctions->call(sParams))
200 throw SyntaxError(SyntaxError::FUNCTION_ERROR, sCmd, sParams, sParams);
203
204 //cerr << 4 << endl;
205 if (findParameter(sParams, "method", '='))
206 {
207 if (getArgAtPos(sParams, findParameter(sParams, "method", '=')) == "rkf45")
208 {
209 odeStepType = gsl_odeiv_step_rkf45;
210 }
211 else if (getArgAtPos(sParams, findParameter(sParams, "method", '=')) == "rk2")
212 {
213 odeStepType = gsl_odeiv_step_rk2;
214 }
215 else if (getArgAtPos(sParams, findParameter(sParams, "method", '=')) == "rkck")
216 {
217 odeStepType = gsl_odeiv_step_rkck;
218 }
219 else if (getArgAtPos(sParams, findParameter(sParams, "method", '=')) == "rk8pd")
220 {
221 odeStepType = gsl_odeiv_step_rk8pd;
222 }
223 else
224 {
225 odeStepType = gsl_odeiv_step_rk4;
226 }
227 }
228 else
229 {
230 odeStepType = gsl_odeiv_step_rk4;
231 }
232 if (findParameter(sParams, "lyapunov"))
233 bCalcLyapunov = true;
234 if (findParameter(sParams, "tol", '='))
235 {
236 int nRes = 0;
237 string sToleranceParams = getArgAtPos(sParams, findParameter(sParams, "tol", '=')+3);
238 if (sToleranceParams.front() == '[' && sToleranceParams.back() == ']')
239 {
240 sToleranceParams.pop_back();
241 sToleranceParams.erase(0,1);
242 }
243 _odeParser->SetExpr(sToleranceParams);
244 v = _odeParser->Eval(nRes);
245 if (nRes > 1)
246 {
247 dRelTolerance = v[0].real();
248 dAbsTolerance = v[1].real();
249 }
250 else
251 {
252 dRelTolerance = v[0].real();
253 dAbsTolerance = v[0].real();
254 }
255 }
256 else
257 {
258 dRelTolerance = 1e-6;
259 dAbsTolerance = 1e-6;
260 }
261 if (findParameter(sParams, "fx0", '='))
262 {
263 int nRes = 0;
264 string sStartValues = getArgAtPos(sParams, findParameter(sParams, "fx0", '=')+3);
265 if (sStartValues.front() == '[' && sStartValues.back() == ']')
266 {
267 sStartValues.pop_back();
268 sStartValues.erase(0,1);
269 }
270 _odeParser->SetExpr(sStartValues);
271 v = _odeParser->Eval(nRes);
272 for (int i = 0; i < nRes; i++)
273 {
274 vStartValues.push_back(v[i].real());
275 }
276 }
277 if (findParameter(sParams, "samples", '='))
278 {
279 _odeParser->SetExpr(getArgAtPos(sParams, findParameter(sParams, "samples", '=')+7));
280 nSamples = intCast(_odeParser->Eval());
281 if (nSamples <= 0)
282 nSamples = 100;
283 }
284
285 if (bCalcLyapunov)
286 {
287 if (nSamples <= 200)
288 nLyapuSamples = nSamples / 10;
289 else if (nSamples <= 1000)
290 nLyapuSamples = nSamples / 20;
291 else
292 nLyapuSamples = nSamples / 100;
293 }
294 //cerr << 5 << endl;
295 vInterval = readAndParseIntervals(sParams, *_odeParser, *_odeData, *_odeFunctions, *_odeSettings, false);
296 //cerr << 6 << endl;
297 if (!vInterval.size() || isnan(vInterval[0]) || isinf(vInterval[0]) || isnan(vInterval[1]) || isinf(vInterval[1]))
299 dt = (vInterval[1]-vInterval[0])/(double)nSamples;
300 t0 = vInterval[0];
301 t1 = vInterval[0];
302 h = dRelTolerance;
303 h2 = dRelTolerance;
304 //cerr << 7 << endl;
305
306 _defVars.vValue[0][0] = t0;
307 t = t0;
308
309 // Dimension des ODE-Systems bestimmen: odesolve dy1 = y2*x, dy2 = sin(y1)
310 _odeParser->SetExpr(sFunc);
312
313 //cerr << sFunc << endl;
314 //cerr << nDimensions << endl;
315 if (_idx.row.isOpenEnd())
316 _idx.row.setRange(0, _idx.row.front() + nSamples);
317
318 if (_idx.col.isOpenEnd())
319 _idx.col.setRange(0, _idx.col.front() + nDimensions + (long long int)bCalcLyapunov*2);
320
321 if (bAllowCacheClearance)
322 {
323 _odeData->deleteBulk(sTarget, 0, _odeData->getLines(sTarget, false) - 1, 0, nDimensions+(long long int)bCalcLyapunov*2);
324 }
325
326 y = new double[nDimensions];
327 if (bCalcLyapunov)
328 y2 = new double[nDimensions];
329
330 // Startwerte festlegen
331 for (int i = 0; i < nDimensions; i++)
332 {
333 if (i < (int)vStartValues.size())
334 y[i] = vStartValues[i];
335 else
336 y[i] = 0.0;
337 if (bCalcLyapunov)
338 {
339 if (i < (int)vStartValues.size())
340 y2[i] = vStartValues[i];
341 else
342 y2[i] = 0.0;
343 if (!i)
344 y2[i] += dist[0];
345 }
346 }
347
348 for (int i = 1; i < nDimensions; i++)
349 sVarDecl += ", y" + toString(i+1);
350 _odeParser->SetExpr(sVarDecl);
351 _odeParser->Eval();
353
354 _odeParser->SetExpr(sFunc);
355
356 // Routinen initialisieren
357 odeStep = gsl_odeiv_step_alloc(odeStepType, nDimensions);
358 odeControl = gsl_odeiv_control_y_new(dAbsTolerance, dRelTolerance);
359 odeEvolve = gsl_odeiv_evolve_alloc(nDimensions);
360
361 gsl_odeiv_system odeSystem = {odeFunction, jacobian, (unsigned)nDimensions, 0};
362
363 if (bCalcLyapunov)
364 {
365 odeStepType_ly = odeStepType;
366 odeStep_ly = gsl_odeiv_step_alloc(odeStepType_ly, nDimensions);
367 odeControl_ly = gsl_odeiv_control_y_new(dAbsTolerance, dRelTolerance);
368 odeEvolve_ly = gsl_odeiv_evolve_alloc(nDimensions);
369
370 odeSystem_ly.function = odeFunction;
371 odeSystem_ly.jacobian = jacobian;
372 odeSystem_ly.dimension = (unsigned)nDimensions;
373 odeSystem_ly.params = 0;
374 }
375
377 NumeReKernel::printPreFmt(toSystemCodePage("|-> " + _lang.get("ODESOLVER_SOLVE_SYSTEM") + " ..."));
378 if (bAllowCacheClearance || !_idx.row.front())
379 _odeData->setHeadLineElement(_idx.col.front(), sTarget, "x");
380
381 _odeData->writeToTable(_idx.row.front(), _idx.col.front(), sTarget, t);
382
383 for (int j = 0; j < nDimensions; j++)
384 {
385 if (_idx.col[j+1] == VectorIndex::INVALID)
386 break;
387
388 if (bAllowCacheClearance || !_idx.row.front())
389 _odeData->setHeadLineElement(_idx.col[1+j], sTarget, "y_"+toString(j+1));
390
391 _odeData->writeToTable(_idx.row.front(), _idx.col[j+1], sTarget, y[j]);
392 }
393
394 if (bCalcLyapunov && (bAllowCacheClearance || !_idx.row.front()) && _idx.col[nDimensions+2] != VectorIndex::INVALID)
395 {
396 _odeData->setHeadLineElement(_idx.col[1+nDimensions], sTarget, "x_lypnv");
397 _odeData->setHeadLineElement(_idx.col[2+nDimensions], sTarget, "lyapunov");
398 }
399
400 // integrieren
401 for (size_t i = 0; i < (size_t)nSamples; i++)
402 {
403 if (time(0) - tTimeControl > 1 && _odeSettings->systemPrints())
404 {
405 NumeReKernel::printPreFmt(toSystemCodePage("\r|-> " + _lang.get("ODESOLVER_SOLVE_SYSTEM") + " ... " + toString((int)(i*100.0/(double)nSamples)) + " %"));
406 }
407 if (NumeReKernel::GetAsyncCancelState())//GetAsyncKeyState(VK_ESCAPE))
408 {
409 NumeReKernel::printPreFmt(" " + toSystemCodePage(_lang.get("COMMON_CANCEL")) + ".\n");
410 gsl_odeiv_evolve_free(odeEvolve);
411 gsl_odeiv_control_free(odeControl);
412 gsl_odeiv_step_free(odeStep);
413
414 if (bCalcLyapunov)
415 {
416 gsl_odeiv_evolve_free(odeEvolve_ly);
417 gsl_odeiv_control_free(odeControl_ly);
418 gsl_odeiv_step_free(odeStep_ly);
419 }
420
421 odeEvolve = 0;
422 odeControl = 0;
423 odeStep = 0;
424
425 if (y)
426 delete[] y;
427 if (y2)
428 delete[] y2;
429
431 }
432
433 if (_idx.row.size() <= i+1)
434 break;
435
436 t1 += dt;
437
438 while (t < t1)
439 {
440 if (GSL_SUCCESS != gsl_odeiv_evolve_apply(odeEvolve, odeControl, odeStep, &odeSystem, &t, t1, &h, y))
441 break;
442 }
443
444 h2 = dRelTolerance;
445
446 while (t2 < t1 && bCalcLyapunov)
447 {
448 if (GSL_SUCCESS != gsl_odeiv_evolve_apply(odeEvolve_ly, odeControl_ly, odeStep_ly, &odeSystem_ly, &t2, t1, &h2, y2))
449 break;
450 }
451
452 if (bCalcLyapunov)
453 {
454 dist[1] = 0.0;
455 for (int n = 0; n < nDimensions; n++)
456 {
457 dist[1] += (y[n]-y2[n])*(y[n]-y2[n]);
458 }
459 dist[1] = sqrt(dist[1]);
460 lyapu[1] = log(dist[1]/dist[0])/dt;
461 lyapu[0] = (i*lyapu[0] + lyapu[1])/(double)(i+1);
462
463 if (!((i+1) % nLyapuSamples) && _idx.col[nDimensions + 2] != VectorIndex::INVALID)
464 {
465 _odeData->writeToTable((i+1)/nLyapuSamples-1, _idx.col[nDimensions+1], sTarget, t1);
466 _odeData->writeToTable((i+1)/nLyapuSamples-1, _idx.col[nDimensions+2], sTarget, lyapu[0]);
467 }
468
469 for (int n = 0; n < nDimensions; n++)
470 {
471 y2[n] = y[n] + (y2[n]-y[n])*dist[0]/dist[1];
472 }
473
474 gsl_odeiv_evolve_reset(odeEvolve_ly);
475 gsl_odeiv_step_reset(odeStep_ly);
476 }
477
478 _odeData->writeToTable(_idx.row[i+1], _idx.col[0], sTarget, t);
479
480 for (int j = 0; j < nDimensions; j++)
481 {
482 if (_idx.col[j+1] == VectorIndex::INVALID)
483 break;
484
485 _odeData->writeToTable(_idx.row[i+1], _idx.col[j+1], sTarget, y[j]);
486 }
487 }
488
489 gsl_odeiv_evolve_free(odeEvolve);
490 gsl_odeiv_control_free(odeControl);
491 gsl_odeiv_step_free(odeStep);
492
493 if (bCalcLyapunov)
494 {
495 gsl_odeiv_evolve_free(odeEvolve_ly);
496 gsl_odeiv_control_free(odeControl_ly);
497 gsl_odeiv_step_free(odeStep_ly);
498 }
499
500 odeEvolve = 0;
501 odeControl = 0;
502 odeStep = 0;
503
504 if (y)
505 delete[] y;
506
507 if (y2)
508 delete[] y2;
509
511 NumeReKernel::printPreFmt(" " + _lang.get("COMMON_SUCCESS") + ".\n");
512 return true;
513}
514
515
This class implements the function definition managing instance.
Definition: define.hpp:74
bool call(std::string &sExpr, int nRecursion=0)
This function searches for known custom definitions in the passed expression and replaces them with t...
Definition: define.cpp:801
std::string get(const std::string &sMessage, const std::vector< std::string > &vTokens) const
This member function returns the language string for the passed language identifier and replaces all ...
Definition: language.cpp:292
This class represents the central memory managing instance. It will handle all tables and clusters,...
void deleteBulk(const std::string &_sCache, int i1, int i2, int j1=0, int j2=0)
int getLines(StringView sTable, bool _bFull=false) const
bool isTable(const std::string &sTable) const
This member function returns, whether the passed table name corresponds to a known table.
bool addTable(const std::string &sCache, const Settings &_option)
This member function creates a new table. It is checked, whether its name is valid and not already us...
bool containsTablesOrClusters(const std::string &sCmdLine)
This member function evaluates, whether the passed command line contains tables or clusters.
void writeToTable(int _nLine, int _nCol, const std::string &_sCache, const mu::value_type &_dData)
bool setHeadLineElement(int _i, const std::string &_sTable, std::string _sHead)
static NumeReKernel * getInstance()
This static member function returns a a pointer to the singleton instance of the kernel.
Definition: kernel.hpp:221
static void printPreFmt(const std::string &__sLine, bool printingEnabled=true)
This member function appends the pre- formatted string to the buffer and informs the terminal that we...
Definition: kernel.cpp:2683
static bool GetAsyncCancelState()
This function is used by the kernel to get informed, when the user pressed ESC or used other means of...
Definition: kernel.cpp:3703
MemoryManager * _odeData
Definition: odesolver.hpp:40
static int nDimensions
Definition: odesolver.hpp:54
static mu::Parser * _odeParser
Definition: odesolver.hpp:53
Settings * _odeSettings
Definition: odesolver.hpp:42
static int odeFunction(double x, const double y[], double dydx[], void *params)
Definition: odesolver.cpp:73
bool solve(const std::string &sCmd)
Definition: odesolver.cpp:94
FunctionDefinitionManager * _odeFunctions
Definition: odesolver.hpp:41
const gsl_odeiv_step_type * odeStepType
Definition: odesolver.hpp:43
gsl_odeiv_step * odeStep
Definition: odesolver.hpp:44
gsl_odeiv_control * odeControl
Definition: odesolver.hpp:45
static mu::varmap_type mVars
Definition: odesolver.hpp:55
gsl_odeiv_evolve * odeEvolve
Definition: odesolver.hpp:46
static int jacobian(double x, const double y[], double dydx[], double dfdt[], void *params)
Definition: odesolver.hpp:49
This class manages the setting values of the internal (kernel) settings of this application.
Definition: settings.hpp:663
bool systemPrints() const
Returns, whether system messages shall be printed to the terminal.
Definition: settings.hpp:1140
Common exception class for all exceptions thrown in NumeRe.
Definition: error.hpp:32
@ NO_INTERVAL_FOR_ODE
Definition: error.hpp:173
@ FUNCTION_ERROR
Definition: error.hpp:110
@ NO_OPTIONS_FOR_ODE
Definition: error.hpp:177
@ NO_EXPRESSION_FOR_ODE
Definition: error.hpp:166
@ STRINGS_MAY_NOT_BE_EVALUATED_WITH_CMD
Definition: error.hpp:206
@ PROCESS_ABORTED_BY_USER
Definition: error.hpp:202
static size_t invalid_position
Definition: error.hpp:235
size_t size() const
This member function returns the size of the indices stored in this class.
Definition: structures.hpp:314
bool isOpenEnd() const
This member function determines, whether the internal index set has an open end.
Definition: structures.hpp:614
void setRange(int nMin, int nMax)
This member function can be used to force the indices stored internally to be in a defined interval....
Definition: structures.hpp:712
int & front()
This member function returns a reference to the first index value stored internally.
Definition: structures.hpp:640
void SetExpr(StringView a_sExpr)
Set the expression. Triggers first time calculation thus the creation of the bytecode and scanning of...
value_type Eval()
Single-value wrapper around the vectorized overload of this member function.
const varmap_type & GetVar() const
Return a map containing the used variables only.
Mathematical expressions parser.
Definition: muParser.h:51
string getDataElements(string &sLine, Parser &_parser, MemoryManager &_data, const Settings &_option, int options)
Searches the passed string for calls to any table or cluster and replaces them with internal vectors ...
Definition: dataaccess.cpp:276
Indices getIndices(StringView sCmd, mu::Parser &_parser, MemoryManager &_data, const Settings &_option)
Wrapper for the new getIndices function interface.
Definition: indices.cpp:49
bool isValidIndexSet(const Indices &_idx)
Definition: dataaccess.hpp:81
Language _lang
Definition: kernel.cpp:39
std::string toSystemCodePage(std::string)
Converts an internal to an external string. Does nothing currently.
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:251
bool isnan(const value_type &v)
Definition: muParserDef.h:379
std::vector< double > real(const std::vector< value_type > &vVec)
bool isinf(const value_type &v)
Definition: muParserDef.h:374
std::map< string_type, value_type * > varmap_type
Type used for storing variables.
Definition: muParserDef.h:273
DefaultVariables _defVars
vector< double > readAndParseIntervals(string &sExpr, Parser &_parser, MemoryManager &_data, FunctionDefinitionManager &_functions, const Settings &_option, bool bEraseInterval)
This function will read the interval syntax in commands and return their values in a single vector.
void StripSpaces(std::string &)
Removes leading and trailing white spaces and tabulator characters.
int findParameter(const std::string &sCmd, const std::string &sParam, const char cFollowing)
This function searches the passed parameter in the passed command string. If something is found,...
Definition: tools.cpp:113
Structure for the four standard variables.
mu::value_type vValue[4][4]
This structure is central for managing the indices of a table or cluster read or write data access....
VectorIndex col
VectorIndex row
long long int intCast(const std::complex< double > &)
Casts the real part of the complex number to an integer and avoids rounding errors.
Definition: tools.cpp:1824
std::string toString(int)
Converts an integer to a string without the Settings bloat.
Match findCommand(StringView sCmd, const std::string &sCommand)
This function is very important for the command handler.
Definition: tools.cpp:1275
string getArgAtPos(const string &sCmd, unsigned int nPos, int extraction)
Extracts a options value at the selected position and applies automatic parsing, if necessary.
Definition: tools.cpp:1598