SDDSlib
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 the algorithms presented in "Numerical Recipes in C" by
7 * Michael Borland (1995).
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 double *ym, *yn;
55 static 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 double *yFinal2;
122 static long i, last_equations = 0;
123
124 if (steps % 2)
125 steps += 1;
126 if (steps < 8)
127 steps = 8;
128
129 if (equations > last_equations) {
130 if (last_equations) {
131 free(yFinal2);
132 }
133 /* allocate arrays for second solution */
134 yFinal2 = tmalloc(sizeof(*yFinal2) * equations);
135 last_equations = equations;
136 }
137
138 mmid(y, dydx, equations, x0, interval, steps, yFinal, derivs);
139 mmid(y, dydx, equations, x0, interval, steps / 2, yFinal2, derivs);
140 for (i = 0; i < equations; i++)
141 yFinal[i] = (4 * yFinal[i] - yFinal2[i]) / 3;
142}
143
144 /**
145 * @brief Integrates ODEs until a condition is met or the interval is reached.
146 *
147 * This function integrates a set of ordinary differential equations using the modified
148 * midpoint method until either the upper limit of the independent variable is reached
149 * or a user-supplied exit condition is satisfied (i.e., a specified function becomes zero).
150 *
151 * @param yif Initial and final values of dependent variables.
152 * @param derivs Function pointer to compute derivatives.
153 * @param n_eq Number of equations in the system.
154 * @param accuracy Desired accuracy for each dependent variable.
155 * @param accmode Desired accuracy-control mode.
156 * @param tiny Ignored parameter.
157 * @param misses Ignored parameter.
158 * @param x0 Initial value of the independent variable (updated to final value).
159 * @param xf Upper limit of integration for the independent variable.
160 * @param x_accuracy Desired accuracy for the final value of the independent variable.
161 * @param h_step Initial step size for integration.
162 * @param h_max Ignored parameter.
163 * @param h_rec Ignored parameter.
164 * @param exit_func Function to determine when to stop integration.
165 * @param exit_accuracy Desired accuracy for the exit condition function.
166 *
167 * @return
168 * - Returns a positive value (>=1) on successful integration.
169 * - Returns 0 or a negative value on failure.
170 * - Specific return values indicate different outcomes, such as zero found or
171 * stepping outside the interval.
172 */
174 double *yif, /* initial/final values of dependent variables */
175 void (*derivs)(double *dydx, double *y, double x), /* (*derivs)(dydx, y, x) */
176 long n_eq, /* number of equations */
177 /* for each dependent variable: */
178 double *accuracy, /* desired accuracy--see below for meaning */
179 long *accmode, /* desired accuracy-control mode */
180 double *tiny, /* ignored */
181 long *misses, /* ignored */
182 /* for the dependent variable: */
183 double *x0, /* initial/final value */
184 double xf, /* upper limit of integration */
185 double x_accuracy, /* accuracy of final value */
186 double h_step, /* step size */
187 double h_max, /* ignored */
188 double *h_rec, /* ignored */
189 /* function for determining when to stop integration: */
190 double (*exit_func)(double *dydx, double *y, double x),
191 /* function that is to be zeroed */
192 double exit_accuracy /* how close to zero to get */
193) {
194 static double *y0, *yscale;
195 static double *dydx0, *y1, *dydx1, *dydx2, *y2, *accur;
196 static long last_neq = 0;
197 double ex0, ex1, ex2, x1, x2;
198 double xdiff;
199 long i, n_exit_iterations;
200#define MAX_N_STEP_UPS 10
201
202 if (*x0 > xf)
203 return (DIFFEQ_XI_GT_XF);
204 if (FABS(*x0 - xf) < x_accuracy)
205 return (DIFFEQ_SOLVED_ALREADY);
206
207 if (last_neq < n_eq) {
208 if (last_neq != 0) {
209 tfree(y0);
210 tfree(dydx0);
211 tfree(y1);
212 tfree(dydx1);
213 tfree(y2);
214 tfree(dydx2);
215 tfree(yscale);
216 tfree(accur);
217 }
218 y0 = tmalloc(sizeof(double) * n_eq);
219 dydx0 = tmalloc(sizeof(double) * n_eq);
220 y1 = tmalloc(sizeof(double) * n_eq);
221 dydx1 = tmalloc(sizeof(double) * n_eq);
222 y2 = tmalloc(sizeof(double) * n_eq);
223 dydx2 = tmalloc(sizeof(double) * n_eq);
224 last_neq = n_eq;
225 }
226
227 for (i = 0; i < n_eq; i++)
228 y0[i] = yif[i];
229
230 /* calculate derivatives and exit function at the initial point */
231 (*derivs)(dydx0, y0, *x0);
232 ex0 = (*exit_func)(dydx0, y0, *x0);
233
234 do {
235 /* check for zero of exit function */
236 if (FABS(ex0) < exit_accuracy) {
237 for (i = 0; i < n_eq; i++)
238 yif[i] = y0[i];
239 return (DIFFEQ_ZERO_FOUND);
240 }
241
242 /* adjust step size to stay within interval */
243 if ((xdiff = xf - *x0) < h_step)
244 h_step = xdiff;
245 /* take a step */
246 x1 = *x0;
247 mmid2(y0, dydx0, n_eq, x1, h_step, 8, y1, derivs);
248 x1 += h_step;
249 /* calculate derivatives and exit function at new point */
250 (*derivs)(dydx1, y1, x1);
251 ex1 = (*exit_func)(dydx1, y1, x1);
252 if (SIGN(ex0) != SIGN(ex1))
253 break;
254 /* check for end of interval */
255 if (FABS(xdiff = xf - x1) < x_accuracy) {
256 /* end of the interval */
257 for (i = 0; i < n_eq; i++)
258 yif[i] = y1[i];
259 *x0 = x1;
260 return (DIFFEQ_END_OF_INTERVAL);
261 }
262 /* copy the new solution into the old variables */
263 SWAP_PTR(dydx0, dydx1);
264 SWAP_PTR(y0, y1);
265 ex0 = ex1;
266 *x0 = x1;
267 } while (1);
268
269 if (!exit_func) {
270 printf("failure in mmid_odeint3_na(): solution stepped outside interval\n");
271 return (DIFFEQ_OUTSIDE_INTERVAL);
272 }
273
274 if (FABS(ex1) < exit_accuracy) {
275 for (i = 0; i < n_eq; i++)
276 yif[i] = y1[i];
277 *x0 = x1;
278 return (DIFFEQ_ZERO_FOUND);
279 }
280
281 /* The root has been bracketed. */
282 n_exit_iterations = MAX_EXIT_ITERATIONS;
283 do {
284 /* try to take a step to the position where the zero is expected */
285 h_step = -ex0 * (x1 - *x0) / (ex1 - ex0) * ITER_FACTOR;
286 x2 = *x0;
287 mmid2(y0, dydx0, n_eq, x2, h_step, 8, y2, derivs);
288 x2 += h_step;
289 /* check the exit function at the new position */
290 (*derivs)(dydx2, y2, x2);
291 ex2 = (*exit_func)(dydx2, y2, x2);
292 if (FABS(ex2) < exit_accuracy) {
293 for (i = 0; i < n_eq; i++)
294 yif[i] = y2[i];
295 *x0 = x2;
296 return (DIFFEQ_ZERO_FOUND);
297 }
298 /* rebracket the root */
299 if (SIGN(ex1) == SIGN(ex2)) {
300 SWAP_PTR(y1, y2);
301 SWAP_PTR(dydx1, dydx2);
302 x1 = x2;
303 ex1 = ex2;
304 } else {
305 SWAP_PTR(y0, y2);
306 SWAP_PTR(dydx0, dydx2);
307 *x0 = x2;
308 ex0 = ex2;
309 }
310 } while (n_exit_iterations--);
311 return (DIFFEQ_EXIT_COND_FAILED);
312}
int tfree(void *ptr)
Frees a memory block and records the deallocation if tracking is enabled.
Definition array.c:230
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
Definition array.c:59
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:173
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