SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
mmid.c
Go to the documentation of this file.
1/**
2 * @file mmid.c
3 * @brief Modified midpoint method for integrating ordinary differential equations (ODEs).
4 *
5 * This file implements the modified midpoint method for integrating ODEs,
6 * based on algorithms presented in "Numerical Recipes in C" by
7 * Press et al.
8 *
9 * @copyright
10 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
11 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
12 *
13 * @license
14 * This file is distributed under the terms of the Software License Agreement
15 * found in the file LICENSE included with this distribution.
16 *
17 * @author M. Borland, C. Saunders, R. Soliday
18 */
19
20#include "mdb.h"
21
22#define MAX_EXIT_ITERATIONS 400
23#define ITER_FACTOR 0.995
24#define TINY 1.0e-30
25
26 /**
27 * @brief Integrates a system of ODEs using the modified midpoint method.
28 *
29 * This function performs numerical integration of a system of ordinary differential
30 * equations using the modified midpoint method. It computes the final values of
31 * the dependent variables after a specified number of steps over a given interval.
32 *
33 * @param yInitial Starting values of dependent variables.
34 * @param dydxInitial Derivatives of the dependent variables at the initial point.
35 * @param equations Number of equations in the system.
36 * @param xInitial Starting value of the independent variable.
37 * @param interval Size of the integration interval in the independent variable.
38 * @param steps Number of steps to divide the interval into.
39 * @param yFinal Array to store the final values of the dependent variables.
40 * @param derivs Function pointer to compute derivatives.
41 */
42void mmid(
43 double *yInitial, /* starting values of dependent variables */
44 double *dydxInitial, /* and their derivatives */
45 long equations, /* number of equations */
46 double xInitial, /* starting value of independent variable */
47 double interval, /* size of interval in x */
48 long steps, /* number of steps to divide interval into */
49 double *yFinal, /* final values of dependent variables */
50 /* function return derivatives */
51 void (*derivs)(double *_dydx, double *_y, double _x)) {
52 long i, j;
53 double x = 0, ynSave, h, hTimes2;
54 static MDB_THREAD_LOCAL double *ym = NULL, *yn = NULL;
55 static MDB_THREAD_LOCAL long last_equations = 0;
56 double *dydxTemp;
57
58 if (equations > last_equations) {
59 if (last_equations) {
60 free(ym);
61 free(yn);
62 }
63 /* allocate arrays for solutions at two adjacent points in x */
64 ym = tmalloc(sizeof(*ym) * equations);
65 yn = tmalloc(sizeof(*yn) * equations);
66 last_equations = equations;
67 }
68
69 hTimes2 = (h = interval / steps) * 2;
70
71 /* Copy starting values and compute first set of estimated values */
72 for (i = 0; i < equations; i++) {
73 ym[i] = yInitial[i];
74 yn[i] = yInitial[i] + h * dydxInitial[i];
75 }
76
77 dydxTemp = yFinal; /* use yFinal for temporary storage */
78 for (j = 1; j < steps; j++) {
79 x = xInitial + h * j;
80 (*derivs)(dydxTemp, yn, x);
81 for (i = 0; i < equations; i++) {
82 ynSave = yn[i];
83 yn[i] = ym[i] + hTimes2 * dydxTemp[i];
84 ym[i] = ynSave;
85 }
86 }
87
88 /* Compute final values */
89 (*derivs)(dydxTemp, yn, x + interval);
90 for (i = 0; i < equations; i++)
91 yFinal[i] = (ym[i] + yn[i] + h * dydxTemp[i]) / 2;
92}
93
94 /**
95 * @brief Enhances the modified midpoint method with error correction.
96 *
97 * This function applies the modified midpoint method with an additional correction step
98 * to improve the accuracy of the integration. It performs integration with a specified
99 * number of steps and then refines the result by integrating with half the number of steps,
100 * combining both results to achieve higher precision.
101 *
102 * @param y Starting values of dependent variables.
103 * @param dydx Derivatives of the dependent variables at the initial point.
104 * @param equations Number of variables in the system.
105 * @param x0 Starting value of the independent variable.
106 * @param interval Size of the integration interval in the independent variable.
107 * @param steps Number of steps to divide the interval into.
108 * @param yFinal Array to store the final values of the dependent variables.
109 * @param derivs Function pointer to compute derivatives.
110 */
111void mmid2(
112 double *y, /* starting values of dependent variables */
113 double *dydx, /* and their derivatives */
114 long equations, /* number of variables */
115 double x0, /* starting value of independent variable */
116 double interval, /* size of interval in x */
117 long steps, /* number of steps to divide interval into */
118 double *yFinal, /* final values of dependent variables */
119 /* function return derivatives */
120 void (*derivs)(double *_dydx, double *_y, double _x)) {
121 static MDB_THREAD_LOCAL double *yFinal2 = NULL;
122 static MDB_THREAD_LOCAL long last_equations = 0;
123 long i;
124
125 if (steps % 2)
126 steps += 1;
127 if (steps < 8)
128 steps = 8;
129
130 if (equations > last_equations) {
131 if (last_equations) {
132 free(yFinal2);
133 }
134 /* allocate arrays for second solution */
135 yFinal2 = tmalloc(sizeof(*yFinal2) * equations);
136 last_equations = equations;
137 }
138
139 mmid(y, dydx, equations, x0, interval, steps, yFinal, derivs);
140 mmid(y, dydx, equations, x0, interval, steps / 2, yFinal2, derivs);
141 for (i = 0; i < equations; i++)
142 yFinal[i] = (4 * yFinal[i] - yFinal2[i]) / 3;
143}
144
145 /**
146 * @brief Integrates ODEs until a condition is met or the interval is reached.
147 *
148 * This function integrates a set of ordinary differential equations using the modified
149 * midpoint method until either the upper limit of the independent variable is reached
150 * or a user-supplied exit condition is satisfied (i.e., a specified function becomes zero).
151 *
152 * @param yif Initial and final values of dependent variables.
153 * @param derivs Function pointer to compute derivatives.
154 * @param n_eq Number of equations in the system.
155 * @param accuracy Desired accuracy for each dependent variable.
156 * @param accmode Desired accuracy-control mode.
157 * @param tiny Ignored parameter.
158 * @param misses Ignored parameter.
159 * @param x0 Initial value of the independent variable (updated to final value).
160 * @param xf Upper limit of integration for the independent variable.
161 * @param x_accuracy Desired accuracy for the final value of the independent variable.
162 * @param h_step Initial step size for integration.
163 * @param h_max Ignored parameter.
164 * @param h_rec Ignored parameter.
165 * @param exit_func Function to determine when to stop integration.
166 * @param exit_accuracy Desired accuracy for the exit condition function.
167 *
168 * @return
169 * - Returns a positive value (>=1) on successful integration.
170 * - Returns 0 or a negative value on failure.
171 * - Specific return values indicate different outcomes, such as zero found or
172 * stepping outside the interval.
173 */
175 double *yif, /* initial/final values of dependent variables */
176 void (*derivs)(double *dydx, double *y, double x), /* (*derivs)(dydx, y, x) */
177 long n_eq, /* number of equations */
178 /* for each dependent variable: */
179 double *accuracy, /* desired accuracy--see below for meaning */
180 long *accmode, /* desired accuracy-control mode */
181 double *tiny, /* ignored */
182 long *misses, /* ignored */
183 /* for the dependent variable: */
184 double *x0, /* initial/final value */
185 double xf, /* upper limit of integration */
186 double x_accuracy, /* accuracy of final value */
187 double h_step, /* step size */
188 double h_max, /* ignored */
189 double *h_rec, /* ignored */
190 /* function for determining when to stop integration: */
191 double (*exit_func)(double *dydx, double *y, double x),
192 /* function that is to be zeroed */
193 double exit_accuracy /* how close to zero to get */
194) {
195 static MDB_THREAD_LOCAL double *y0 = NULL, *yscale = NULL;
196 static MDB_THREAD_LOCAL double *dydx0 = NULL, *y1 = NULL, *dydx1 = NULL, *dydx2 = NULL, *y2 = NULL, *accur = NULL;
197 static MDB_THREAD_LOCAL long last_neq = 0;
198 double ex0, ex1, ex2, x1, x2;
199 double xdiff;
200 long i, n_exit_iterations;
201#define MAX_N_STEP_UPS 10
202
203 if (*x0 > xf)
204 return (DIFFEQ_XI_GT_XF);
205 if (FABS(*x0 - xf) < x_accuracy)
206 return (DIFFEQ_SOLVED_ALREADY);
207
208 if (last_neq < n_eq) {
209 if (last_neq != 0) {
210 tfree(y0);
211 tfree(dydx0);
212 tfree(y1);
213 tfree(dydx1);
214 tfree(y2);
215 tfree(dydx2);
216 tfree(yscale);
217 tfree(accur);
218 }
219 y0 = tmalloc(sizeof(double) * n_eq);
220 dydx0 = tmalloc(sizeof(double) * n_eq);
221 y1 = tmalloc(sizeof(double) * n_eq);
222 dydx1 = tmalloc(sizeof(double) * n_eq);
223 y2 = tmalloc(sizeof(double) * n_eq);
224 dydx2 = tmalloc(sizeof(double) * n_eq);
225 last_neq = n_eq;
226 }
227
228 for (i = 0; i < n_eq; i++)
229 y0[i] = yif[i];
230
231 /* calculate derivatives and exit function at the initial point */
232 (*derivs)(dydx0, y0, *x0);
233 ex0 = (*exit_func)(dydx0, y0, *x0);
234
235 do {
236 /* check for zero of exit function */
237 if (FABS(ex0) < exit_accuracy) {
238 for (i = 0; i < n_eq; i++)
239 yif[i] = y0[i];
240 return (DIFFEQ_ZERO_FOUND);
241 }
242
243 /* adjust step size to stay within interval */
244 if ((xdiff = xf - *x0) < h_step)
245 h_step = xdiff;
246 /* take a step */
247 x1 = *x0;
248 mmid2(y0, dydx0, n_eq, x1, h_step, 8, y1, derivs);
249 x1 += h_step;
250 /* calculate derivatives and exit function at new point */
251 (*derivs)(dydx1, y1, x1);
252 ex1 = (*exit_func)(dydx1, y1, x1);
253 if (SIGN(ex0) != SIGN(ex1))
254 break;
255 /* check for end of interval */
256 if (FABS(xdiff = xf - x1) < x_accuracy) {
257 /* end of the interval */
258 for (i = 0; i < n_eq; i++)
259 yif[i] = y1[i];
260 *x0 = x1;
261 return (DIFFEQ_END_OF_INTERVAL);
262 }
263 /* copy the new solution into the old variables */
264 SWAP_PTR(dydx0, dydx1);
265 SWAP_PTR(y0, y1);
266 ex0 = ex1;
267 *x0 = x1;
268 } while (1);
269
270 if (!exit_func) {
271 printf("failure in mmid_odeint3_na(): solution stepped outside interval\n");
272 return (DIFFEQ_OUTSIDE_INTERVAL);
273 }
274
275 if (FABS(ex1) < exit_accuracy) {
276 for (i = 0; i < n_eq; i++)
277 yif[i] = y1[i];
278 *x0 = x1;
279 return (DIFFEQ_ZERO_FOUND);
280 }
281
282 /* The root has been bracketed. */
283 n_exit_iterations = MAX_EXIT_ITERATIONS;
284 do {
285 /* try to take a step to the position where the zero is expected */
286 h_step = -ex0 * (x1 - *x0) / (ex1 - ex0) * ITER_FACTOR;
287 x2 = *x0;
288 mmid2(y0, dydx0, n_eq, x2, h_step, 8, y2, derivs);
289 x2 += h_step;
290 /* check the exit function at the new position */
291 (*derivs)(dydx2, y2, x2);
292 ex2 = (*exit_func)(dydx2, y2, x2);
293 if (FABS(ex2) < exit_accuracy) {
294 for (i = 0; i < n_eq; i++)
295 yif[i] = y2[i];
296 *x0 = x2;
297 return (DIFFEQ_ZERO_FOUND);
298 }
299 /* rebracket the root */
300 if (SIGN(ex1) == SIGN(ex2)) {
301 SWAP_PTR(y1, y2);
302 SWAP_PTR(dydx1, dydx2);
303 x1 = x2;
304 ex1 = ex2;
305 } else {
306 SWAP_PTR(y0, y2);
307 SWAP_PTR(dydx0, dydx2);
308 *x0 = x2;
309 ex0 = ex2;
310 }
311 } while (n_exit_iterations--);
312 return (DIFFEQ_EXIT_COND_FAILED);
313}
int tfree(void *ptr)
Frees a memory block and records the deallocation if tracking is enabled.
Definition array.c:243
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:65
long mmid_odeint3_na(double *yif, void(*derivs)(double *dydx, double *y, double x), long n_eq, double *accuracy, long *accmode, double *tiny, long *misses, double *x0, double xf, double x_accuracy, double h_step, double h_max, double *h_rec, double(*exit_func)(double *dydx, double *y, double x), double exit_accuracy)
Integrates ODEs until a condition is met or the interval is reached.
Definition mmid.c:174
void mmid(double *yInitial, double *dydxInitial, long equations, double xInitial, double interval, long steps, double *yFinal, void(*derivs)(double *_dydx, double *_y, double _x))
Integrates a system of ODEs using the modified midpoint method.
Definition mmid.c:42
void mmid2(double *y, double *dydx, long equations, double x0, double interval, long steps, double *yFinal, void(*derivs)(double *_dydx, double *_y, double _x))
Enhances the modified midpoint method with error correction.
Definition mmid.c:111