The Conley-Morse Graphs Software
odeapprox.h
Go to the documentation of this file.
1/////////////////////////////////////////////////////////////////////////////
2///
3/// @file odeapprox.h
4///
5/// A generic method for computing a time-t map for ODEs
6/// in an approximate way.
7///
8/// This file defines a generic class which uses a non-rigorous integration
9/// method based on Runge-Kutta applied to sample points in an interval box
10/// in order to compute an approximation of a time-t map for ODEs.
11///
12/// @author Pawel Pilarczyk
13///
14/////////////////////////////////////////////////////////////////////////////
15
16// Copyright (C) 1997-2014 by Pawel Pilarczyk.
17//
18// This file is part of my research software package. This is free software:
19// you can redistribute it and/or modify it under the terms of the GNU
20// General Public License as published by the Free Software Foundation,
21// either version 3 of the License, or (at your option) any later version.
22//
23// This software is distributed in the hope that it will be useful,
24// but WITHOUT ANY WARRANTY; without even the implied warranty of
25// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26// GNU General Public License for more details.
27//
28// You should have received a copy of the GNU General Public License
29// along with this software; see the file "license.txt". If not,
30// please, see <https://www.gnu.org/licenses/>.
31
32// Started on December 16, 2010. Last revision: May 30, 2011.
33
34
35#ifndef _CMGRAPHS_ODEAPPROX_H_
36#define _CMGRAPHS_ODEAPPROX_H_
37
38
39// include some standard C++ header files
40#include <string>
41#include <vector>
42#include <iostream>
43
44// include local header files
45#include "maptype.h"
46#include "odetimet.h"
47
48// include all the necessary header files from the CHomP library
49#include "chomp/system/config.h"
50#include "chomp/system/textfile.h"
51
52
53// --------------------------------------------------
54// ----------------- the time-t map -----------------
55// --------------------------------------------------
56
57/// This class defines a map for the nonlinear density dependent
58/// overcompensatory Leslie population model.
59/// The interpretation of parameters depends on the dimension.
61{
62public:
63 /// The constructor in which one sets the step size
64 /// and the number of steps for the non-rigorous integration,
65 /// the number of sample points in each direction,
66 /// and the margin to add around the box (as a fraction
67 /// of its actual width).
68 MapOdeApprox (double step, int nSteps, int nSamples,
69 double margin = 0.01);
70
71 /// The destructor.
73
74 /// Sets the parameters to the given intervals defined by their
75 /// left and right coordinates.
76 /// This function calls the original function from the
77 /// class MapType, and skips the function from MapOdeTimeT.
78 void setParam (const double *left, const double *right, int n);
79
80 /// Computes the image of a box whose left and right coordinates
81 /// are given. Fills in the images with the left and right
82 /// coordinates of the image box.
83 void compute (const double *xleft, const double *xright,
84 double *yleft, double *yright, int dim,
85 const spcCoord *coord, int subdiv) const;
86
87private:
88 /// The size of the step for itegrating the flow.
89 double step_;
90
91 /// The number of steps to use.
93
94 /// The number of samples in each direction.
96
97 /// The margin to add around the box (a fraction of its width).
98 double margin_;
99
100 /// The vector field. Parameters (if any) should be determined
101 /// using the methods of the class MapType: getLeftParam
102 /// and getRightParam.
103 virtual void vectorField (double t, const double *x, double *y,
104 int dim) const = 0;
105
106 /// The copy constructor should not be used.
108
109 /// The assignment operator should not be used.
111
112 /// The Runge-Kutta method for iterating a point,
113 /// using the vector field defined in this class.
114 /// The buffer should be preallocated for 5*dim entries.
115 void RungeKutta (double t, double *x, double step, int dim,
116 double *buffer) const;
117
118 /// Computes the ranges of coordinates.
119 static void computeRanges (const double *samples, int nSamples,
120 int dim, double *minimal, double *maximal);
121
122 /// Makes a correction to the vector field to make it fade
123 /// gradually outside the bounding box if one is in defined
124 /// and in use for the parent class MapOdeTimeT.
125 void fadeVectorField (const double *x, double *y, int dim) const;
126
127}; /* class MapOdeApprox */
128
129// --------------------------------------------------
130
131inline MapOdeApprox::MapOdeApprox (double step, int nSteps, int nSamples,
132 double margin):
133 MapOdeTimeT ("", 0, 0, 0),
134 step_ (step), nSteps_ (nSteps), nSamples_ (nSamples),
135 margin_ (margin)
136{
137// chomp::homology::sbug << "DEBUG: MapOdeApprox constructor.\n";
138 if (step_ <= 0)
139 throw "Non-positive step requested in MapOdeApprox.";
140 if (nSteps_ <= 0)
141 {
142 throw "Non-positive number of steps requested "
143 "in MapOdeApprox.";
144 }
145 if (nSamples_ <= 0)
146 {
147 throw "Non-positive number of samples requested "
148 "in MapOdeApprox.";
149 }
150 static bool displayed = false;
151 if (!displayed)
152 {
153 chomp::homology::sbug << "ODE approx: " << nSteps <<
154 " steps of " << step << " (1/" << (1.0 / step) <<
155 ") each, " << nSamples <<
156 " samples in each dir.\n";
157 displayed = true;
158 }
159 return;
160} /* MapOdeApprox::MapOdeApprox */
161
163{
164// chomp::homology::sbug << "DEBUG: MapOdeApprox destructor.\n";
165 return;
166} /* MapOdeApprox::~MapOdeApprox */
167
168inline void MapOdeApprox::setParam (const double *left, const double *right,
169 int n)
170{
171 // set the new values of parameters (skip the MapOdeTimeT method)
172 MapType::setParam (left, right, n);
173
174 return;
175} /* MapOdeApprox::setParam */
176
177inline void MapOdeApprox::fadeVectorField (const double *x, double *y,
178 int dim) const
179{
180 if (!boundingBox || (boundingBoxDim != dim))
181 return;
182
183 // a fraction of the width of the bounding box for the fading
184 const double fadeBegin = 0.1;
185 const double fadeEnd = 0.2;
186
187 // the sides of the bounding box (left0, right0, left1, right1, etc.)
188 const double *sides = boundingBox;
189
190 // make the fading in each direction separately
191 for (int i = 0; i < dim; ++ i)
192 {
193 // determine the left and right sides of the bounding box
194 double left = * (sides ++);
195 double right = * (sides ++);
196
197 // compute the distance from the box if the point is outside
198 double distance = 0;
199 if (x [i] < left)
200 distance = left - x [i];
201 else if (x [i] > right)
202 distance = x [i] - right;
203 else
204 continue;
205
206 // if the distance is too small then skip this case
207 double distBegin = fadeBegin * (right - left);
208 if (distance <= distBegin)
209 continue;
210
211 // fade the vector field outside the box
212 double distEnd = fadeEnd * (right - left);
213 if (distance >= distEnd)
214 y [i] = 0;
215 else
216 {
217 y [i] *= (distEnd - distance) /
218 (distEnd - distBegin);
219 }
220 }
221
222 return;
223} /* MapOdeApprox::fadeVectorField */
224
225inline void MapOdeApprox::RungeKutta (double t, double *x, double step,
226 int dim, double *buffer) const
227{
228 double *k1 = buffer;
229 double *k2 = k1 + dim;
230 double *k3 = k2 + dim;
231 double *k4 = k3 + dim;
232 double *tmp = k4 + dim;
233
234 vectorField (t, x, k1, dim);
235 fadeVectorField (x, k1, dim);
236
237 for (int i = 0; i < dim; ++ i)
238 tmp [i] = x [i] + 0.5 * step * k1 [i];
239 vectorField (t + 0.5 * step, tmp, k2, dim);
240 fadeVectorField (tmp, k2, dim);
241
242 for (int i = 0; i < dim; ++ i)
243 tmp [i] = x [i] + 0.5 * step * k2 [i];
244 vectorField (t + 0.5 * step, tmp, k3, dim);
245 fadeVectorField (tmp, k3, dim);
246
247 for (int i = 0; i < dim; ++ i)
248 tmp [i] = x [i] + step * k3 [i];
249 vectorField (t + step, tmp, k4, dim);
250 fadeVectorField (tmp, k4, dim);
251
252 for (int i = 0; i < dim; ++ i)
253 {
254 x [i] += step / 6 * (k1 [i] + 2 * k2 [i] +
255 2 * k3 [i] + k4 [i]);
256 }
257
258 return;
259} /* MapOdeApprox::RungeKutta */
260
261inline void MapOdeApprox::computeRanges (const double *samples, int nSamples,
262 int dim, double *minimal, double *maximal)
263{
264 const double *sample = samples;
265 for (int n = 0; n < nSamples; ++ n)
266 {
267 for (int i = 0; i < dim; ++ i)
268 {
269 if (!n || (minimal [i] > sample [i]))
270 minimal [i] = sample [i];
271 if (!n || (maximal [i] < sample [i]))
272 maximal [i] = sample [i];
273 }
274 sample += dim;
275 }
276 return;
277} /* MapOdeApprox::computeRanges */
278
279inline void MapOdeApprox::compute (const double *xleft, const double *xright,
280 double *yleft, double *yright, int dim, const spcCoord *, int) const
281{
282 // for debugging only
283 using chomp::homology::sbug;
284
285 // calculate the number of points to iterate
286 int countPoints = nSamples_;
287 int nSamplesInterior = (nSamples_ > 2) ? (nSamples_ - 2) : 0;
288 int interiorPoints = nSamplesInterior;
289 for (int i = 1; i < dim; ++ i)
290 {
291 countPoints *= nSamples_;
292 interiorPoints *= nSamplesInterior;
293 }
294 countPoints -= interiorPoints;
295
296 // allocate an array for the coordinates of sample points to iterate
297 double *samples = new double [(countPoints + 5) * dim];
298 double *buffer = samples + (countPoints * dim);
299
300 // compute the coordinates of the sample points to iterate
301 int *counters = new int [dim];
302 for (int i = 0; i < dim; ++ i)
303 counters [i] = nSamples_ - 1;
304 int curPoint = 0;
305 double *curSample = samples;
306 while (curPoint < countPoints)
307 {
308 // increase the multi-dimensional counter
309 for (int curIndex = 0; curIndex < dim; ++ curIndex)
310 {
311 if (++ counters [curIndex] < nSamples_)
312 break;
313 counters [curIndex] = 0;
314 }
315
316 // skip this counter if this is not a boundary point
317 bool boundary = false;
318 for (int i = 0; i < dim; ++ i)
319 {
320 if ((counters [i] == 0) ||
321 (counters [i] == nSamples_ - 1))
322 {
323 boundary = true;
324 break;
325 }
326 }
327 // debug begin
328 // sbug << "DEBUG: Counters " << curPoint << ":";
329 // for (int i = 0; i < dim; ++ i)
330 // sbug << ' ' << counters [i];
331 // sbug << (boundary ? " [bd]\n" : " [int]\n");
332 // debug end
333 if (!boundary)
334 continue;
335
336 // compute the coordinates of the point
337 for (int i = 0; i < dim; ++ i)
338 {
339 if (counters [i] == 0)
340 *curSample = xleft [i];
341 else if (counters [i] == nSamples_ - 1)
342 *curSample = xright [i];
343 else
344 {
345 double fraction = static_cast<double>
346 (counters [i]) / (nSamples_ - 1);
347 *curSample = xleft [i] + fraction *
348 (xright [i] - xleft [i]);
349 }
350 ++ curSample;
351 }
352 ++ curPoint;
353 }
354 // debug check: the counter should be reset to the original one
355 for (int i = 0; i < dim; ++ i)
356 {
357 if (counters [i] != nSamples_ - 1)
358 throw "Non-zero counter, bad programming, sorry!";
359 }
360 delete [] counters;
361
362 // iterate the points
363// int counter = 0;
364 bool interrupted = false;
365 for (int step = 0; step < nSteps_; ++ step)
366 {
367 // move the set of sample points one step forward
368 double t = step * step_;
369 double *sample = samples;
370 for (int n = 0; n < countPoints; ++ n)
371 {
372 RungeKutta (t, sample, step_, dim, buffer);
373 // checkOutside (sample, dim);
374 sample += dim;
375 }
376
377 // check if the set went outside the given region
378 /* if (++ counter >= checkEvery)
379 {
380 // compute the ranges of the coordinates
381 computeRanges (samples, countPoints, dim,
382 yleft, yright);
383 interrupted = checkOutside (yleft, yright, dim);
384 if (interrupted)
385 break;
386 counter = 0;
387 }
388 */
389 }
390
391 // compute the ranges of the coordinates of the set of points
392 if (!interrupted)
393 computeRanges (samples, countPoints, dim, yleft, yright);
394
395 // add the margin around the resulting box
396 if (margin_ > 0)
397 {
398 for (int i = 0; i < dim; ++ i)
399 {
400 double collar = (yright [i] - yleft [i]) * margin_;
401 yleft [i] -= collar;
402 yright [i] += collar;
403 }
404 }
405
406 delete [] samples;
407 return;
408} /* MapOdeApprox::compute */
409
410
411#endif // _CMGRAPHS_ODEAPPROX_H_
412
This class defines a map for the nonlinear density dependent overcompensatory Leslie population model...
Definition: odeapprox.h:61
void setParam(const double *left, const double *right, int n)
Sets the parameters to the given intervals defined by their left and right coordinates.
Definition: odeapprox.h:168
void RungeKutta(double t, double *x, double step, int dim, double *buffer) const
The Runge-Kutta method for iterating a point, using the vector field defined in this class.
Definition: odeapprox.h:225
double step_
The size of the step for itegrating the flow.
Definition: odeapprox.h:89
MapOdeApprox & operator=(const MapOdeApprox &)
The assignment operator should not be used.
~MapOdeApprox()
The destructor.
Definition: odeapprox.h:162
void fadeVectorField(const double *x, double *y, int dim) const
Makes a correction to the vector field to make it fade gradually outside the bounding box if one is i...
Definition: odeapprox.h:177
MapOdeApprox(const MapOdeApprox &)
The copy constructor should not be used.
MapOdeApprox(double step, int nSteps, int nSamples, double margin=0.01)
The constructor in which one sets the step size and the number of steps for the non-rigorous integrat...
Definition: odeapprox.h:131
double margin_
The margin to add around the box (a fraction of its width).
Definition: odeapprox.h:98
int nSamples_
The number of samples in each direction.
Definition: odeapprox.h:95
virtual void vectorField(double t, const double *x, double *y, int dim) const =0
The vector field.
static void computeRanges(const double *samples, int nSamples, int dim, double *minimal, double *maximal)
Computes the ranges of coordinates.
Definition: odeapprox.h:261
void compute(const double *xleft, const double *xright, double *yleft, double *yright, int dim, const spcCoord *coord, int subdiv) const
Computes the image of a box whose left and right coordinates are given.
Definition: odeapprox.h:279
int nSteps_
The number of steps to use.
Definition: odeapprox.h:92
This class defines a map for the nonlinear density dependent overcompensatory Leslie population model...
Definition: odetimet.h:82
int boundingBoxDim
The dimension of the bounding box.
Definition: odetimet.h:168
double * boundingBox
A bounding box for the area where the trajectories are computed.
Definition: odetimet.h:165
virtual void setParam(const double *left, const double *right, int n)
Sets the parameters to the given intervals defined by their left and right coordinates.
Definition: maptype.h:172
An abstract map type.
A generic method for computing a time-t map for ODEs.
int spcCoord
The type of coordinates of cubes in the phase space.
Definition: typespace.h:50