libode
Easy to compile, fast ODE integrators as C++ classes
Loading...
Searching...
No Matches
ode_base.cc
Go to the documentation of this file.
1
2
3#include "ode_base.h"
4
5namespace ode {
6
7OdeBase::OdeBase (unsigned long neq, bool need_jac) {
8
9 //default system name
10 name_ = "ode";
11 //whether stuff should be printed during a solve
12 quiet_ = false;
14 silent_snap_ = false;
15 //number of equations/variables in ode system
16 neq_ = neq;
17 //time starts at zero unless modified
18 t_ = 0.0;
19 //time step
20 dt_ = NAN;
21 //number of time steps
22 nstep_ = 0;
23 //number of function evaluations
24 neval_ = 0;
25 //interval of solution integrity checks
26 icheck_ = 100;
27 //array of solution values for a given time
28 sol_ = new double[neq];
29 //store flag
30 need_jac_ = need_jac;
31 //counter for jacobian evaluations
32 nJac_ = 0;
33 //only allocate space for Jacobian if it's needed
34 if (need_jac_) {
35 //allocate space for the jacobian of the system
36 Jac_ = new double*[neq];
37 for (unsigned long i=0; i<neq; i++) Jac_[i] = new double[neq];
38 //arrays for numerical Jacobian
39 f_ = new double[neq];
40 g_ = new double[neq];
41 //adjustment factors for numerical jacobian
42 absjacdel_ = 1e-8;
43 reljacdel_ = 1e-8;
44 }
45}
46
48 delete [] sol_;
49 if (need_jac_) {
50 for (unsigned long i=0; i<neq_; i++) delete [] Jac_[i];
51 delete [] Jac_;
52 delete [] f_;
53 delete [] g_;
54 }
55}
56
57//----------------------------------------------------------------------------
58//use finite dif to approximate Jacobian unless virtual function reimplemented
59
60void OdeBase::ode_jac (double *solin, double **Jout) {
61
62 //check space was allocated for Jacobian
63 if ( !need_jac_ ) ode_print_exit("the Jacobian matrix wasn't allocated for whichever solver was chosen (need_jac_ == false)");
64
65 unsigned long i,j;
66 double delsol;
67
68 //get derivatives at current position
69 ode_fun_(solin, f_);
70 //perturb and calculate finite differences
71 for (i=0; i<neq_; i++) {
72 //perturb
73 delsol = ode_max2(absjacdel_, solin[i]*reljacdel_);
74 solin[i] += delsol;
75 //evaluate function
76 ode_fun_(solin, g_);
77 //compute approximate derivatives
78 for (j=0; j<neq_; j++) Jout[j][i] = (g_[j] - f_[j])/delsol;
79 //unperturb
80 solin[i] -= delsol;
81 }
82}
83
84//-----------------------------------
85//essential virtual function wrappers
86
87void OdeBase::ode_fun_ (double *solin, double *fout) {
88
89 //call the system of equations
90 ode_fun(solin, fout);
91 //increment the counter
92 neval_++;
93}
94
95void OdeBase::ode_jac_ (double *solin, double **Jout) {
96
97 //call the Jacobian function
98 ode_jac(solin, Jout);
99 //increment the counter
100 nJac_++;
101}
102
103void OdeBase::step (double dt, bool extra) {
104
105 //store the time step for access in derived classes
106 dt_ = dt;
107 //call the bare stepper
108 step_(dt);
109 //increment the time, a convenience variable
110 t_ += dt;
111 //increment the counter
112 nstep_++;
113 //check for nans and infs
114 if (nstep_ % icheck_ == 0) check_sol_integrity();
115 //do any extra stuff
116 if (extra) after_step(t_);
117}
118
119//--------------
120//solver support
121
122void OdeBase::snap (std::string dirout, long isnap, double tsnap) {
123
124 if (silent_snap_) {
125 //progress report
126 if (!quiet_) printf(" snap %li reached\n", isnap);
127 //do any extra snapping stuff
128 after_snap(dirout, isnap, tsnap);
129 } else {
130 //output filename
131 std::string fnout = dirout + "/" + name_ + "_snap_" + ode_int_to_string(isnap);
132 //write output
133 ode_write(fnout.data(), sol_, neq_);
134 //progress report
135 if (!quiet_) printf(" snap %li written\n", isnap);
136 //do any extra snapping stuff
137 after_snap(dirout, isnap, tsnap);
138 }
139}
140
141bool OdeBase::solve_done (double dt, double tend) {
142
143 //if the next time step will put the time very close to or above the
144 //stopping time, the main iteration is finished and a final time step
145 //can be taken to get exactly to the stopping time
146 if ( (t_ + dt*1.01) >= tend )
147 return(true);
148 return(false);
149}
150
152
153 for ( unsigned long i=0; i<neq_; i++ ) {
154 //any nans?
155 if ( std::isnan(sol_[i]) ) ode_print_exit("solution vector contains NaN");
156 //any infs?
157 if ( !std::isfinite(sol_[i]) ) ode_print_exit("solution vector contains Inf");
158 }
159}
160
161void OdeBase::check_pre_solve (double tint, double dt) {
162
163 //shouldn't be solving backward
164 if (tint <= 0.0) ode_print_exit("tint must be greater than zero");
165 //shouldn't be solving backward
166 if (dt <= 0.0) ode_print_exit("dt must be greater than or equal to 0");
167}
168
169void OdeBase::check_pre_snaps (double dt, double *tsnap, unsigned long nsnap) {
170
171 if (dt <= 0) ode_print_exit("dt must be greater than or equal to 0");
172 if (nsnap <= 1) ode_print_exit("nsnap must be greater than 1");
173 for (unsigned long i=0; i<nsnap; i++) {
174 if (tsnap[i] < t_) ode_print_exit("snap times must be greater than or equal to the current time");
175 if (i > 0) if (tsnap[i] <= tsnap[i-1]) ode_print_exit("snap times must monotonically increase");
176 }
177}
178
179//----------------
180//solver functions
181
182void OdeBase::solve_fixed_ (double tint, double dt, bool extra) {
183
184 //stopping time
185 double tend = t_ + tint;
186 //solve to within a time step of tend
187 while ( !solve_done(dt, tend) ) step(dt, extra);
188 //solve a fractional time step to finish
189 step(tend - t_);
190}
191
192void OdeBase::solve_fixed (double tint, double dt, bool extras) {
193
194 //checks
195 check_pre_solve(tint, dt);
196 if (extras) {
197 //extra initial things (to be overridden in derived class)
198 before_solve();
199 //solve
200 solve_fixed_(tint, dt, true);
201 //extra completion things (to be overridden in derived class)
202 after_solve();
203 } else {
204 solve_fixed_(tint, dt, false);
205 }
206}
207
208void OdeBase::solve_fixed (double tint, double dt, const char* dirout, int inter) {
209
210 //checks
211 check_pre_solve(tint, dt);
212 if (inter < 1) ode_print_exit("inter must be greater than or equal to 1");
213 //indices
214 unsigned long i, j;
215 //output file strings
216 std::string fnout;
217 dirout_ = dirout;
218 //stopping time
219 double tend = t_ + tint;
220
221 //output vectors
222 std::vector<double> tout;
223 std::vector< std::vector<double> > solout;
224 solout.resize(neq_);
225
226 //extra initial things
227 before_solve();
228
229 //store values at time zero
230 for (i=0; i<neq_; i++) solout[i].push_back( sol_[i] );
231 tout.push_back( t_ );
232 //solve while storing solution values
233 j = 0;
234 while ( !solve_done(dt, tend) ) {
235 //take a step
236 step(dt);
237 //capture values
238 if ( j % inter == 0 ) {
240 for (i=0; i<neq_; i++) solout[i].push_back( sol_[i] );
241 tout.push_back( t_ );
242 }
243 j++;
244 }
245 //take a final step
246 step(tend - t_);
247 //store final values
248 for (i=0; i<neq_; i++) solout[i].push_back( sol_[i] );
249 tout.push_back( t_ );
250
251 //write output
252 for (i=0; i<neq_; i++) {
253 fnout = dirout_ + "/" + name_ + "_" + ode_int_to_string(i);
254 ode_write(fnout.c_str(), solout[i].data(), solout[i].size());
255 }
256 fnout = dirout_ + "/" + name_ + "_t";
257 ode_write(fnout.c_str(), tout.data(), tout.size());
258
259 //extra completion things (to be overridden in derived class)
260 after_solve();
261
262 //clear output directory
263 dirout_ = "";
264}
265
266void OdeBase::solve_fixed (double tint, double dt, unsigned long nsnap, const char *dirout) {
267
268 //checks
269 check_pre_solve(tint, dt);
270 //index
271 unsigned long i;
272 //stopping time
273 double tend = t_ + tint;
274 //make an array of the snap times
275 double *tsnap = new double[nsnap];
276 for (i=0; i<nsnap; i++) tsnap[i] = t_ + i*(tend - t_)/double(nsnap - 1);
277 //call the other snapping function with the computed snap times
278 solve_fixed(dt, tsnap, nsnap, dirout);
279 //free the snap times
280 delete [] tsnap;
281}
282
283void OdeBase::solve_fixed (double dt, double *tsnap, unsigned long nsnap, const char *dirout) {
284
285 //checks
286 check_pre_snaps(dt, tsnap, nsnap);
287 //index
288 unsigned long i;
289 //store the output directory
290 dirout_ = dirout;
291
292 //extra initial things (to be overridden in derived class)
293 before_solve();
294
295 //solve to each snap time and take a snap
296 for (i=0; i<nsnap; i++) {
297 solve_fixed_(tsnap[i] - t_, dt);
298 snap(dirout, i, t_);
299 }
300 //write the snap times
301 if ( !silent_snap_ )
302 ode_write((dirout_ + "/" + name_ + "_snap_t").data(), tsnap, nsnap);
303
304 //extra completion things
305 after_solve();
306
307 //clear output directory
308 dirout_ = "";
309}
310
311//-----
312//reset
313
314void OdeBase::reset (double t, double *sol) {
315
316 t_ = t;
317 for (unsigned long i=0; i<neq_; i++) sol_[i] = sol[i];
318}
319
320//------
321//extras
322
323void OdeBase::before_solve () { /* virtual, left to derived class */ }
324
325void OdeBase::after_step (double t) {
326 /* virtual, left to derived class, can only be used with solve_fixed() */
327 (void)t;
328}
329
330void OdeBase::after_capture (double t) {
331 /* virtual, left to derived class, can only be used with solve_fixed() */
332 (void)t;
333}
334
335void OdeBase::after_snap (std::string dirout, long isnap, double t) {
336 /* virtual, left to derived class */
337 (void)dirout;
338 (void)isnap;
339 (void)t;
340}
341
342void OdeBase::after_solve () { /* virtual, left to derived class */ }
343
344} // namespace ode
void ode_jac_(double *solin, double **Jout)
wrapper, calls ode_jac() and increments nJac;
Definition ode_base.cc:95
virtual void after_capture(double t)
does any extra stuff only when a step is captured
Definition ode_base.cc:330
double reljacdel_
relative adjustment fraction for numerical Jacobian, if needed
Definition ode_base.h:347
OdeBase(unsigned long neq, bool need_jac)
constructs
Definition ode_base.cc:7
long unsigned icheck_
interval of steps after which to check for nans and infs (zero to ignore)
Definition ode_base.h:339
unsigned long neq_
number of equations in the system of ODEs
Definition ode_base.h:327
void step(double dt, bool extra=true)
increments the step counter and the time, checks the solution integrity if needed,...
Definition ode_base.cc:103
void solve_fixed(double tint, double dt, bool extras=true)
integrates for a specified duration of independent variable without output
Definition ode_base.cc:192
bool solve_done(double dt, double tend)
checks if the solution is within a single time step of the end point
Definition ode_base.cc:141
double t_
time, initialized to zero
Definition ode_base.h:329
long unsigned nJac_
counter for jacobian evaluations
Definition ode_base.h:343
std::string name_
the "name" of the system, which is used for output
Definition ode_base.h:317
virtual void ode_jac(double *solin, double **Jout)
evaluates the system's Jacobian matrix, also in autonomous form, and can either be defined in a deriv...
Definition ode_base.cc:60
virtual void after_snap(std::string dirout, long isnap, double t)
does any extra stuff after each snap
Definition ode_base.cc:335
virtual void before_solve()
does any extra stuff before starting a solve
Definition ode_base.cc:323
virtual void after_solve()
does any extra stuff after completing a solve
Definition ode_base.cc:342
double dt_
time step is stored and updated during solves
Definition ode_base.h:331
void snap(std::string dirout, long isnap, double tsnap)
writes the current value of the solution to a binary file
Definition ode_base.cc:122
virtual ~OdeBase()
destructs
Definition ode_base.cc:47
long unsigned nstep_
number of time steps
Definition ode_base.h:335
virtual void step_(double dt)=0
advances a single time step (without changing counters or the time) and must be defined in the derive...
bool quiet_
whether stuff should be printed during a solve
Definition ode_base.h:323
virtual void ode_fun(double *solin, double *fout)=0
evaluates the system of ODEs in autonomous form and must be defined by a derived class
void check_sol_integrity()
checks solution for nans and infs, exiting the program if they're found
Definition ode_base.cc:151
double ** Jac_
storage for the ODE system's Jacobian matrix, only allocated for the methods that need it
Definition ode_base.h:341
void reset(double t, double *sol)
reset to a specified time and initial condition array
Definition ode_base.cc:314
void solve_fixed_(double tint, double dt, bool extra=true)
integrates without output or any counters, trackers, extra functions...
Definition ode_base.cc:182
bool silent_snap_
whether to skip writing the solution vector to file when snapping but still execute after_snap()
Definition ode_base.h:325
double absjacdel_
absolute adjustment fraction for numerical Jacobian, if needed
Definition ode_base.h:345
void check_pre_snaps(double dt, double *tsnap, unsigned long nsnap)
checks that snap times are monotonically increasing and > current time
Definition ode_base.cc:169
double * sol_
array for the solution, changing over time
Definition ode_base.h:333
void ode_fun_(double *solin, double *fout)
wrapper, calls ode_fun() and increases the neval counter by one
Definition ode_base.cc:87
virtual void after_step(double t)
does any extra stuff after each step
Definition ode_base.cc:325
long unsigned neval_
function evaluation counter, must be incremented in step() when defined
Definition ode_base.h:337
void check_pre_solve(double tint, double dt)
checks that a solve can be performed with given tend and dt values
Definition ode_base.cc:161
std::string dirout_
output directory if one is being used by a solver
Definition ode_base.h:321
double ode_max2(double a, double b)
Simple maximum of two doubles.
Definition ode_util.cc:7
void ode_print_exit(const char *msg)
print a message and exit with failure
Definition ode_io.cc:17
void ode_write(char const *fn, T *a, unsigned long n)
write an array to a binary file
Definition ode_io.h:27
std::string ode_int_to_string(long i)
converts an integer into a string
Definition ode_io.cc:25