LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
ode_explicit.c
浏览该文件的文档.
1
5#include <math.h>
6
7#include "memory_bridge.h"
8#include "internal.h"
9#include "ode_internal.h"
10#include "lmmc/ode.h"
11
23
24static void explicit_free(ode_explicit_state_t* s, size_t stages) {
26 while (stages > 0) {
27 --stages;
28 lmmc_memory_free(s->k[stages]);
29 }
30}
31
32static lmmc_status_t explicit_allocate(ode_explicit_state_t* s, size_t stages) {
33 int allocated = 1;
34 for (size_t stage = 0; stage < stages; ++stage) {
35 s->k[stage] = (lmmc_real_t*)lmmc_memory_alloc(s->work_bytes);
36 if (s->k[stage] == NULL) { allocated = 0; }
37 }
39 if (s->y_tmp == NULL) { allocated = 0; }
40 if (!allocated) {
41 explicit_free(s, stages);
44 }
45 return LMMC_STATUS_OK;
46}
47
48static lmmc_status_t explicit_rhs(
49 ode_explicit_state_t* s, lmmc_real_t t, const lmmc_real_t* y, lmmc_real_t* derivative
50) {
51 int callback_failed = 0;
52 lmmc_status_t st = lmmc_ode_rhs_eval(s->rhs, t, y, derivative, s->dim,
53 s->user_data, &s->out_result->num_rhs_evals, &callback_failed);
54 if (st != LMMC_STATUS_OK) {
55 s->out_result->failure_reason = callback_failed ?
57 }
58 return st;
59}
60
61static lmmc_status_t explicit_advance(ode_explicit_state_t* s) {
62 if (!lmmc_ode_state_is_finite(s->y, s->dim)) {
65 }
66 LMMC_REAL_ADD(&s->t, &s->t, &s->h);
67 if (!lmmc_is_finite(&s->t)) {
70 }
71 s->out_result->num_steps += 1;
72 s->out_result->final_t = s->t;
73 lmmc_ode_do_log(&s->local_cfg, s->out_result->num_steps, s->t, s->y, s->dim);
74 return LMMC_STATUS_OK;
75}
76
77static lmmc_status_t explicit_finish(ode_explicit_state_t* s, lmmc_real_t t_end) {
78 if (s->t >= t_end) {
79 s->out_result->converged = 1;
81 } else {
82 s->out_result->converged = 0;
84 }
86}
87
88static lmmc_status_t euler_integrate(ode_explicit_state_t* s, lmmc_real_t t_end) {
89 while (s->t < t_end && s->out_result->num_steps < s->local_cfg.max_steps) {
90 size_t i = 0;
91 lmmc_real_t rem;
93
94 LMMC_REAL_SUB(&rem, &t_end, &s->t);
95 if (rem <= 0.0 || !lmmc_is_finite(&rem)) {
96 break;
97 }
98
99 if (s->h > rem) {
100 s->h = rem;
101 }
102
103 st = explicit_rhs(s, s->t, s->y, s->k[0]);
104 if (st != LMMC_STATUS_OK) { return st; }
105 for (i = 0; i < s->dim; ++i) {
106 lmmc_real_t tmp;
107 LMMC_REAL_MUL(&tmp, &s->h, &s->k[0][i]);
108 LMMC_REAL_ADD(&s->y[i], &s->y[i], &tmp);
109 }
110 st = explicit_advance(s);
111 if (st != LMMC_STATUS_OK) { return st; }
112 }
113 return explicit_finish(s, t_end);
114}
115
117 lmmc_ode_rhs_t rhs,
118 void* user_data,
119 size_t dim,
120 lmmc_real_t t_start,
121 lmmc_real_t t_end,
122 lmmc_real_t* y,
123 const lmmc_ode_config_t* cfg,
124 lmmc_ode_result_t* out_result
125) {
126 ode_explicit_state_t state = {0};
127 ode_explicit_state_t* s = &state;
128 s->rhs = rhs;
129 s->user_data = user_data;
130 s->dim = dim;
131 s->y = y;
132 s->out_result = out_result;
133 s->t = t_start;
134 lmmc_status_t st = validate_and_init_ode_config(rhs, dim, t_start, t_end, y,
135 cfg, &s->local_cfg, out_result, &s->work_bytes);
136 if (st != LMMC_STATUS_OK) { return st; }
138 if (s->k[0] == NULL) {
141 }
142 s->h = lmmc_clamp(s->local_cfg.initial_step, s->local_cfg.min_step, s->local_cfg.max_step);
143 {
144 lmmc_real_t span;
145 LMMC_REAL_SUB(&span, &t_end, &t_start);
146 if (s->h > span) {
147 s->h = span;
148 }
149 }
150
151 lmmc_ode_do_log(&s->local_cfg, 0, s->t, s->y, s->dim);
152 st = euler_integrate(s, t_end);
153 lmmc_memory_free(s->k[0]);
154 return st;
155}
156
157/*
158 * Cash-Karp embedded Runge-Kutta 4(5) pair.
159 * 6 stages, 4th-order solution for stepping, 5th-order for error estimation.
160 *
161 * Butcher tableau nodes (c_i):
162 */
163static const lmmc_real_t ck_c[6] = {
164 0.0,
165 1.0 / 5.0,
166 3.0 / 10.0,
167 3.0 / 5.0,
168 1.0,
169 7.0 / 8.0
170};
171
172/*
173 * Butcher tableau matrix (a_ij), stored row-major.
174 * a[i][j] for j < i. Only non-zero entries listed per row.
175 */
176static const lmmc_real_t ck_a21 = 1.0 / 5.0;
177
178static const lmmc_real_t ck_a31 = 3.0 / 40.0;
179static const lmmc_real_t ck_a32 = 9.0 / 40.0;
180
181static const lmmc_real_t ck_a41 = 3.0 / 10.0;
182static const lmmc_real_t ck_a42 = -9.0 / 10.0;
183static const lmmc_real_t ck_a43 = 6.0 / 5.0;
184
185static const lmmc_real_t ck_a51 = -11.0 / 54.0;
186static const lmmc_real_t ck_a52 = 5.0 / 2.0;
187static const lmmc_real_t ck_a53 = -70.0 / 27.0;
188static const lmmc_real_t ck_a54 = 35.0 / 27.0;
189
190static const lmmc_real_t ck_a61 = 1631.0 / 55296.0;
191static const lmmc_real_t ck_a62 = 175.0 / 512.0;
192static const lmmc_real_t ck_a63 = 575.0 / 13824.0;
193static const lmmc_real_t ck_a64 = 44275.0 / 110592.0;
194static const lmmc_real_t ck_a65 = 253.0 / 4096.0;
195
196/* 5th-order weights (for the error estimation solution) */
197static const lmmc_real_t ck_b5[6] = {
198 37.0 / 378.0,
199 0.0,
200 250.0 / 621.0,
201 125.0 / 594.0,
202 0.0,
203 512.0 / 1771.0
204};
205
206/* Error coefficients: e_i = b5_i - b4_i */
207static const lmmc_real_t ck_e[6] = {
208 37.0 / 378.0 - 2825.0 / 27648.0,
209 0.0,
210 250.0 / 621.0 - 18575.0 / 48384.0,
211 125.0 / 594.0 - 13525.0 / 55296.0,
212 0.0 - 277.0 / 14336.0,
213 512.0 / 1771.0 - 1.0 / 4.0
214};
215
216static lmmc_status_t cash_karp_stages(ode_explicit_state_t* s) {
217 size_t i;
218 lmmc_status_t st;
219 st = explicit_rhs(s, s->t, s->y, s->k[0]);
220 if (st != LMMC_STATUS_OK) { return st; }
221
222 for (i = 0; i < s->dim; ++i) {
223 s->y_tmp[i] = s->y[i] + s->h * ck_a21 * s->k[0][i];
224 }
225 st = explicit_rhs(s, s->t + ck_c[1] * s->h, s->y_tmp, s->k[1]);
226 if (st != LMMC_STATUS_OK) { return st; }
227
228 for (i = 0; i < s->dim; ++i) {
229 s->y_tmp[i] = s->y[i] + s->h * (ck_a31 * s->k[0][i] + ck_a32 * s->k[1][i]);
230 }
231 st = explicit_rhs(s, s->t + ck_c[2] * s->h, s->y_tmp, s->k[2]);
232 if (st != LMMC_STATUS_OK) { return st; }
233
234 for (i = 0; i < s->dim; ++i) {
235 s->y_tmp[i] = s->y[i] + s->h * (ck_a41 * s->k[0][i] + ck_a42 * s->k[1][i] + ck_a43 * s->k[2][i]);
236 }
237 st = explicit_rhs(s, s->t + ck_c[3] * s->h, s->y_tmp, s->k[3]);
238 if (st != LMMC_STATUS_OK) { return st; }
239
240 for (i = 0; i < s->dim; ++i) {
241 s->y_tmp[i] = s->y[i] + s->h * (ck_a51 * s->k[0][i] + ck_a52 * s->k[1][i] +
242 ck_a53 * s->k[2][i] + ck_a54 * s->k[3][i]);
243 }
244 st = explicit_rhs(s, s->t + ck_c[4] * s->h, s->y_tmp, s->k[4]);
245 if (st != LMMC_STATUS_OK) { return st; }
246
247 for (i = 0; i < s->dim; ++i) {
248 s->y_tmp[i] = s->y[i] + s->h * (ck_a61 * s->k[0][i] + ck_a62 * s->k[1][i] +
249 ck_a63 * s->k[2][i] + ck_a64 * s->k[3][i] +
250 ck_a65 * s->k[4][i]);
251 }
252 st = explicit_rhs(s, s->t + ck_c[5] * s->h, s->y_tmp, s->k[5]);
253 if (st != LMMC_STATUS_OK) { return st; }
254 return LMMC_STATUS_OK;
255}
256
257static lmmc_real_t cash_karp_error(ode_explicit_state_t* s) {
258 size_t i;
259 lmmc_real_t err_norm;
260 /* 先构造候选解, 再用新旧状态共同缩放嵌入误差. */
261 err_norm = 0.0;
262 for (i = 0; i < s->dim; ++i) {
263 lmmc_real_t err_i = s->h * (ck_e[0] * s->k[0][i] + ck_e[2] * s->k[2][i] +
264 ck_e[3] * s->k[3][i] + ck_e[4] * s->k[4][i] +
265 ck_e[5] * s->k[5][i]);
266 lmmc_real_t scale;
267 lmmc_real_t ratio;
268 s->y_tmp[i] = s->y[i] + s->h * (ck_b5[0] * s->k[0][i] + ck_b5[2] * s->k[2][i] +
269 ck_b5[3] * s->k[3][i] + ck_b5[5] * s->k[5][i]);
270 scale = s->local_cfg.abs_tol +
271 s->local_cfg.rel_tol * fmax(fabs(s->y[i]), fabs(s->y_tmp[i]));
272 ratio = err_i / scale;
273 err_norm += ratio * ratio;
274 }
275 err_norm = sqrt(err_norm / (lmmc_real_t)s->dim);
276 return err_norm;
277}
278
279static lmmc_status_t cash_karp_control(ode_explicit_state_t* s, lmmc_real_t err_norm, int step_accepted) {
280 lmmc_real_t h_new;
281 if (err_norm > 0.0 && isfinite(err_norm)) {
282 lmmc_real_t factor = s->local_cfg.adaptive_step_beta * pow(err_norm, -0.2);
283 factor = lmmc_clamp(factor, 0.2, 5.0);
284 h_new = lmmc_clamp(s->h * factor, s->local_cfg.min_step, s->local_cfg.max_step);
285 } else {
286 h_new = lmmc_clamp(s->h * 5.0, s->local_cfg.min_step, s->local_cfg.max_step);
287 }
288
289 if (!step_accepted) {
291 if (h_new <= s->local_cfg.min_step && err_norm > 1.0) {
292 if (s->h <= s->local_cfg.min_step) {
295 }
296 }
297 }
298
299 s->h = h_new;
300 return LMMC_STATUS_OK;
301}
302
303static lmmc_status_t cash_karp_integrate(ode_explicit_state_t* s, lmmc_real_t t_end) {
304 size_t attempts = 0;
305 while (s->t < t_end && attempts < s->local_cfg.max_steps) {
306 lmmc_real_t rem;
308 lmmc_real_t err_norm = 0.0;
309 int step_accepted = 0;
310 ++attempts;
311
312 LMMC_REAL_SUB(&rem, &t_end, &s->t);
313 if (rem <= 0.0 || !lmmc_is_finite(&rem)) {
314 break;
315 }
316
317 if (s->h > rem) {
318 s->h = rem;
319 }
320 st = cash_karp_stages(s);
321 if (st != LMMC_STATUS_OK) { return st; }
322 err_norm = cash_karp_error(s);
323 if (err_norm <= 1.0) {
324 step_accepted = 1;
325 memcpy(s->y, s->y_tmp, s->work_bytes);
326 st = explicit_advance(s);
327 if (st != LMMC_STATUS_OK) { return st; }
328 }
329 st = cash_karp_control(s, err_norm, step_accepted);
330 if (st != LMMC_STATUS_OK) { return st; }
331 }
332 return explicit_finish(s, t_end);
333}
334
336 lmmc_ode_rhs_t rhs,
337 void* user_data,
338 size_t dim,
339 lmmc_real_t t_start,
340 lmmc_real_t t_end,
341 lmmc_real_t* y,
342 const lmmc_ode_config_t* cfg,
343 lmmc_ode_result_t* out_result
344) {
345 ode_explicit_state_t state = {0};
346 ode_explicit_state_t* s = &state;
347 s->rhs = rhs;
348 s->user_data = user_data;
349 s->dim = dim;
350 s->y = y;
351 s->out_result = out_result;
352 s->t = t_start;
353 lmmc_status_t st = validate_and_init_ode_config(rhs, dim, t_start, t_end, y,
354 cfg, &s->local_cfg, out_result, &s->work_bytes);
355 if (st != LMMC_STATUS_OK) { return st; }
356 st = explicit_allocate(s, 6);
357 if (st != LMMC_STATUS_OK) { return st; }
358 s->h = lmmc_clamp(s->local_cfg.initial_step, s->local_cfg.min_step, s->local_cfg.max_step);
359 {
360 lmmc_real_t span;
361 LMMC_REAL_SUB(&span, &t_end, &t_start);
362 if (s->h > span) {
363 s->h = span;
364 }
365 }
366
367 lmmc_ode_do_log(&s->local_cfg, 0, s->t, s->y, s->dim);
368 st = cash_karp_integrate(s, t_end);
369 explicit_free(s, 6);
370 return st;
371}
372
373static lmmc_status_t rk4_step(ode_explicit_state_t* s) {
374 size_t i;
375 lmmc_status_t st;
376 lmmc_real_t half_h;
377 lmmc_real_t t_mid;
378 lmmc_real_t t_next;
379 lmmc_real_t half = 0.5;
380 lmmc_real_t two = 2.0;
381 lmmc_real_t six = 6.0;
382 lmmc_real_t h_over_6;
383 st = explicit_rhs(s, s->t, s->y, s->k[0]);
384 if (st != LMMC_STATUS_OK) { return st; }
385
386 LMMC_REAL_MUL(&half_h, &half, &s->h);
387 for (i = 0; i < s->dim; ++i) {
388 lmmc_real_t tmp;
389 LMMC_REAL_MUL(&tmp, &half_h, &s->k[0][i]);
390 LMMC_REAL_ADD(&s->y_tmp[i], &s->y[i], &tmp);
391 }
392
393 LMMC_REAL_ADD(&t_mid, &s->t, &half_h);
394 st = explicit_rhs(s, t_mid, s->y_tmp, s->k[1]);
395 if (st != LMMC_STATUS_OK) { return st; }
396
397 for (i = 0; i < s->dim; ++i) {
398 lmmc_real_t tmp;
399 LMMC_REAL_MUL(&tmp, &half_h, &s->k[1][i]);
400 LMMC_REAL_ADD(&s->y_tmp[i], &s->y[i], &tmp);
401 }
402
403 st = explicit_rhs(s, t_mid, s->y_tmp, s->k[2]);
404 if (st != LMMC_STATUS_OK) { return st; }
405
406 for (i = 0; i < s->dim; ++i) {
407 lmmc_real_t tmp;
408 LMMC_REAL_MUL(&tmp, &s->h, &s->k[2][i]);
409 LMMC_REAL_ADD(&s->y_tmp[i], &s->y[i], &tmp);
410 }
411
412 LMMC_REAL_ADD(&t_next, &s->t, &s->h);
413 st = explicit_rhs(s, t_next, s->y_tmp, s->k[3]);
414 if (st != LMMC_STATUS_OK) { return st; }
415
416 LMMC_REAL_DIV(&h_over_6, &s->h, &six);
417 for (i = 0; i < s->dim; ++i) {
418 lmmc_real_t t2k2, t2k3, sum1, sum2, sum3, weighted;
419 LMMC_REAL_MUL(&t2k2, &two, &s->k[1][i]);
420 LMMC_REAL_MUL(&t2k3, &two, &s->k[2][i]);
421 LMMC_REAL_ADD(&sum1, &s->k[0][i], &t2k2);
422 LMMC_REAL_ADD(&sum2, &sum1, &t2k3);
423 LMMC_REAL_ADD(&sum3, &sum2, &s->k[3][i]);
424 LMMC_REAL_MUL(&weighted, &h_over_6, &sum3);
425 LMMC_REAL_ADD(&s->y[i], &s->y[i], &weighted);
426 }
427 return explicit_advance(s);
428}
429
430static lmmc_status_t rk4_integrate(ode_explicit_state_t* s, lmmc_real_t t_end) {
431 while (s->t < t_end && s->out_result->num_steps < s->local_cfg.max_steps) {
432 lmmc_real_t rem;
434 LMMC_REAL_SUB(&rem, &t_end, &s->t);
435 if (rem <= 0.0 || !lmmc_is_finite(&rem)) {
436 break;
437 }
438
439 if (s->h > rem) {
440 s->h = rem;
441 }
442 st = rk4_step(s);
443 if (st != LMMC_STATUS_OK) { return LMMC_STATUS_NUMERICAL_FAILURE; }
444 }
445 return explicit_finish(s, t_end);
446}
447
449 lmmc_ode_rhs_t rhs,
450 void* user_data,
451 size_t dim,
452 lmmc_real_t t_start,
453 lmmc_real_t t_end,
454 lmmc_real_t* y,
455 const lmmc_ode_config_t* cfg,
456 lmmc_ode_result_t* out_result
457) {
458 ode_explicit_state_t state = {0};
459 ode_explicit_state_t* s = &state;
460 s->rhs = rhs;
461 s->user_data = user_data;
462 s->dim = dim;
463 s->y = y;
464 s->out_result = out_result;
465 s->t = t_start;
466 lmmc_status_t st = validate_and_init_ode_config(rhs, dim, t_start, t_end, y,
467 cfg, &s->local_cfg, out_result, &s->work_bytes);
468 if (st != LMMC_STATUS_OK) { return st; }
469 st = explicit_allocate(s, 4);
470 if (st != LMMC_STATUS_OK) { return st; }
471 s->h = lmmc_clamp(s->local_cfg.initial_step, s->local_cfg.min_step, s->local_cfg.max_step);
472 {
473 lmmc_real_t span;
474 LMMC_REAL_SUB(&span, &t_end, &t_start);
475 if (s->h > span) {
476 s->h = span;
477 }
478 }
479
480 lmmc_ode_do_log(&s->local_cfg, 0, s->t, s->y, s->dim);
481 st = rk4_integrate(s, t_end);
482 explicit_free(s, 4);
483 return st;
484}
#define LMMC_REAL_ADD(res, a, b)
计算 *res = *a + *b 。
#define LMMC_REAL_DIV(res, a, b)
计算 *res = *a / *b 。
#define LMMC_REAL_SUB(res, a, b)
计算 *res = *a - *b 。
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_REAL_MUL(res, a, b)
计算 *res = *a * *b 。
LMMC 内部使用的公共工具(仅源文件可见).
void * lmmc_memory_alloc(size_t size)
void lmmc_memory_free(void *pointer)
Private recoverable allocator for persistent LMMC ownership.
常微分方程初值问题求解器:Euler、RK4、RK45(自适应)。
size_t num_rhs_evals
定义 ode.h:49
lmmc_status_t(*) lmmc_ode_rhs_t(lmmc_real_t t, const lmmc_real_t *y, lmmc_real_t *y_prime, size_t dim, void *user_data)
ODE 右端函数签名 。
定义 ode.h:26
lmmc_real_t initial_step
定义 ode.h:92
@ LMMC_ODE_FAILURE_NUMERICAL_ISSUE
定义 ode.h:40
@ LMMC_ODE_FAILURE_INVALID_STEP
定义 ode.h:38
@ LMMC_ODE_FAILURE_NONE
定义 ode.h:36
@ LMMC_ODE_FAILURE_RHS_EVAL_FAILED
定义 ode.h:41
@ LMMC_ODE_FAILURE_MAX_STEPS
定义 ode.h:39
lmmc_real_t final_t
定义 ode.h:50
lmmc_real_t abs_tol
定义 ode.h:95
lmmc_status_t lmmc_ode_rk4_solve(lmmc_ode_rhs_t rhs, void *user_data, size_t dim, lmmc_real_t t_start, lmmc_real_t t_end, lmmc_real_t *y, const lmmc_ode_config_t *cfg, lmmc_ode_result_t *out_result)
经典四阶 Runge-Kutta 法求解 (固定步长)。
lmmc_real_t rel_tol
定义 ode.h:96
lmmc_ode_failure_t failure_reason
定义 ode.h:51
lmmc_status_t lmmc_ode_rk45_solve(lmmc_ode_rhs_t rhs, void *user_data, size_t dim, lmmc_real_t t_start, lmmc_real_t t_end, lmmc_real_t *y, const lmmc_ode_config_t *cfg, lmmc_ode_result_t *out_result)
自适应 Runge-Kutta-Fehlberg (RK45) 求解器。
lmmc_real_t max_step
定义 ode.h:94
lmmc_status_t lmmc_ode_euler_solve(lmmc_ode_rhs_t rhs, void *user_data, size_t dim, lmmc_real_t t_start, lmmc_real_t t_end, lmmc_real_t *y, const lmmc_ode_config_t *cfg, lmmc_ode_result_t *out_result)
显式 Euler 法求解常微分方程初值问题 。
lmmc_real_t adaptive_step_beta
定义 ode.h:98
lmmc_real_t min_step
定义 ode.h:93
ODE 求解配置。
定义 ode.h:91
ODE 求解结果统计。
定义 ode.h:46
lmmc_ode_config_t local_cfg
lmmc_ode_result_t * out_result
ODE 求解器共享的内部辅助函数。
lmmc_status_t lmmc_ode_rhs_eval(lmmc_ode_rhs_t rhs, lmmc_real_t t, const lmmc_real_t *y, lmmc_real_t *y_prime, size_t dim, void *user_data, size_t *io_eval_count, int *out_callback_failed)
lmmc_status_t validate_and_init_ode_config(lmmc_ode_rhs_t rhs, size_t dim, lmmc_real_t t_start, lmmc_real_t t_end, const lmmc_real_t *y, const lmmc_ode_config_t *cfg, lmmc_ode_config_t *out_cfg, lmmc_ode_result_t *out_result, size_t *out_work_bytes)
int lmmc_ode_state_is_finite(const lmmc_real_t *y, size_t dim)
void lmmc_ode_do_log(const lmmc_ode_config_t *cfg, size_t step, lmmc_real_t t, const lmmc_real_t *y, size_t dim)
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_CONVERGENCE_FAILED
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_ALLOCATION_FAILED