LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
ode_singly_diagonally_implicit.c
浏览该文件的文档.
1
8#include <math.h>
9#include <string.h>
10
11#include "memory_bridge.h"
12#include "internal.h"
13#include "ode_internal.h"
14#include "lmmc/ode.h"
15#include "lmmc/dense.h"
16#include "lmmc/linear_algebra.h"
17
18#define SDIRK_STAGES 5
19#define SDIRK_VECTORS 16
20#define SDIRK_NEWTON_MAX 12
21
22static const lmmc_real_t sdirk_gamma = 0.25;
23static const lmmc_real_t sdirk_c[SDIRK_STAGES] = {0.25, 0.75, 0.55, 0.5, 1.0};
24static const lmmc_real_t sdirk_a[SDIRK_STAGES][SDIRK_STAGES] = {
25 {0.25, 0.0, 0.0, 0.0, 0.0},
26 {0.5, 0.25, 0.0, 0.0, 0.0},
27 {0.34, -0.04, 0.25, 0.0, 0.0},
28 {371.0 / 1360.0, -137.0 / 2720.0, 15.0 / 544.0, 0.25, 0.0},
29 {25.0 / 24.0, -49.0 / 48.0, 125.0 / 16.0, -85.0 / 12.0, 0.25}
30};
31static const lmmc_real_t sdirk_b[SDIRK_STAGES] = {
32 25.0 / 24.0, -49.0 / 48.0, 125.0 / 16.0, -85.0 / 12.0, 0.25
33};
34static const lmmc_real_t sdirk_bhat[SDIRK_STAGES] = {
35 59.0 / 48.0, -17.0 / 96.0, 225.0 / 32.0, -85.0 / 12.0, 0.0
36};
37
38
39static lmmc_status_t sdirk_linear_solve(
40 lmmc_mat_t* matrix,
41 size_t* pivots,
42 lmmc_real_t* rhs,
43 lmmc_real_t* solution,
44 size_t dim
45) {
46 size_t swaps = 0;
47 const lmmc_vec_t rhs_vec = {dim, rhs, 0};
48 lmmc_vec_t solution_vec = {dim, solution, 0};
49 lmmc_status_t st = lmmc_lu_decompose_inplace(matrix, pivots, &swaps);
50 if (st != LMMC_STATUS_OK) { return st; }
51 st = lmmc_lu_solve(matrix, pivots, &rhs_vec, &solution_vec);
52 if (st != LMMC_STATUS_OK) { return st; }
53 return lmmc_ode_values_are_finite(solution, dim);
54}
55
72
73static lmmc_status_t sdirk_newton_correction(ode_sdirk_state_t* s, size_t stage) {
74 size_t i, j;
75 int callback_failed = 0;
79 s->t + sdirk_c[stage] * s->h, s->y_stage, s->f_stage, s->dim,
80 s->matrix_data, s->rhs_sum, s->y_pert, s->f_pert,
81 &s->out_result->num_rhs_evals, &callback_failed});
82 if (st != LMMC_STATUS_OK) {
84 callback_failed ? LMMC_ODE_FAILURE_RHS_EVAL_FAILED
86 return st;
87 }
88 for (i = 0; i < s->dim; ++i) {
89 for (j = 0; j < s->dim; ++j) {
90 s->matrix.data[i * s->dim + j] = -s->h * sdirk_gamma * s->matrix_data[i * s->dim + j];
91 }
92 s->matrix.data[i * s->dim + i] += 1.0;
93 s->residual[i] = -s->residual[i];
94 }
95 st = sdirk_linear_solve(&s->matrix, s->pivots, s->residual, s->delta, s->dim);
96 if (st != LMMC_STATUS_OK) { return st; }
97 for (i = 0; i < s->dim; ++i) s->k[stage][i] += s->delta[i];
98 return LMMC_STATUS_OK;
99}
100
101static lmmc_status_t sdirk_stage_norm(
102 ode_sdirk_state_t* s, const lmmc_real_t* derivative, lmmc_real_t* out_norm)
103{
104 size_t i;
105 int zero_scale = 0;
106 lmmc_status_t st;
107 for (i = 0; i < s->dim; ++i) s->error[i] = s->h * derivative[i];
108 st = lmmc_ode_weighted_rms(s->error, s->y, s->y_stage, s->dim,
109 s->local_cfg.abs_tol, s->local_cfg.rel_tol, out_norm);
110 if (st != LMMC_STATUS_NUMERICAL_FAILURE) return st;
111 for (i = 0; i < s->dim; ++i) {
112 const lmmc_real_t scale = s->local_cfg.abs_tol +
113 s->local_cfg.rel_tol * fmax(fabs(s->y[i]), fabs(s->y_stage[i]));
114 if (!isfinite(scale) || !isfinite(s->error[i])) {
116 }
117 if (scale == 0.0) zero_scale = 1;
118 }
120 if (zero_scale) {
121 *out_norm = INFINITY;
122 return LMMC_STATUS_OK;
123 }
124 return st;
125}
126
127static lmmc_status_t sdirk_stage_residual(
128 ode_sdirk_state_t* s, size_t stage, int* residual_zero)
129{
130 size_t i;
131 int callback_failed = 0;
132 lmmc_status_t st;
133 for (i = 0; i < s->dim; ++i) {
134 s->y_stage[i] = s->y[i] + s->h * (s->rhs_sum[i] + sdirk_gamma * s->k[stage][i]);
135 if (!isfinite(s->y_stage[i])) { return LMMC_STATUS_NUMERICAL_FAILURE; }
136 }
137 st = lmmc_ode_rhs_eval(s->rhs, s->t + sdirk_c[stage] * s->h, s->y_stage, s->f_stage,
138 s->dim, s->user_data, &s->out_result->num_rhs_evals, &callback_failed);
139 if (st != LMMC_STATUS_OK) {
142 return st;
143 }
144 *residual_zero = 1;
145 for (i = 0; i < s->dim; ++i) {
146 s->residual[i] = s->k[stage][i] - s->f_stage[i];
147 if (!isfinite(s->residual[i])) { return LMMC_STATUS_NUMERICAL_FAILURE; }
148 if (s->residual[i] != 0.0) { *residual_zero = 0; }
149 }
150 return LMMC_STATUS_OK;
151}
152
153static lmmc_status_t sdirk_solve_stage(ode_sdirk_state_t* s, size_t stage) {
154 const lmmc_real_t eta = 0.001;
155 size_t i, j, corrections = 0;
156 lmmc_status_t st;
157 memset(s->rhs_sum, 0, s->work_bytes);
158 for (j = 0; j < stage; ++j) {
159 for (i = 0; i < s->dim; ++i) {
160 s->rhs_sum[i] += sdirk_a[stage][j] * s->k[j][i];
161 }
162 }
163 if (stage == 0) { memset(s->k[stage], 0, s->work_bytes); }
164 else { memcpy(s->k[stage], s->k[stage - 1], s->work_bytes); }
165
166 for (;;) {
167 int residual_zero;
168 st = sdirk_stage_residual(s, stage, &residual_zero);
169 if (st != LMMC_STATUS_OK) { return st; }
170 if (residual_zero) { return LMMC_STATUS_OK; }
172 if (corrections != 0) {
173 lmmc_real_t residual_norm, correction_norm;
174 st = sdirk_stage_norm(s, s->residual, &residual_norm);
175 if (st != LMMC_STATUS_OK) { return st; }
176 st = sdirk_stage_norm(s, s->delta, &correction_norm);
177 if (st != LMMC_STATUS_OK) { return st; }
178 if (residual_norm <= eta && correction_norm <= eta) { return LMMC_STATUS_OK; }
179 }
180 if (corrections == SDIRK_NEWTON_MAX) {
183 }
184 st = sdirk_newton_correction(s, stage);
185 if (st != LMMC_STATUS_OK) { return st; }
186 ++corrections;
187 }
188}
189
190static lmmc_status_t sdirk_estimate_error(ode_sdirk_state_t* s, lmmc_real_t* error_norm) {
191 size_t i, j, stage;
192 int callback_failed = 0;
193 lmmc_status_t st;
194 for (i = 0; i < s->dim; ++i) {
195 s->y_new[i] = s->y[i];
196 s->y_hat[i] = s->y[i];
197 for (stage = 0; stage < SDIRK_STAGES; ++stage) {
198 s->y_new[i] += s->h * sdirk_b[stage] * s->k[stage][i];
199 s->y_hat[i] += s->h * sdirk_bhat[stage] * s->k[stage][i];
200 }
201 s->error[i] = s->y_new[i] - s->y_hat[i];
202 }
205 return st;
206 }
207
208 st = lmmc_ode_rhs_eval(s->rhs, s->t + s->h, s->y_new, s->f_stage, s->dim, s->user_data,
209 &s->out_result->num_rhs_evals, &callback_failed);
210 if (st != LMMC_STATUS_OK) {
213 return st;
214 }
216 s->rhs, s->local_cfg.jacobian, s->user_data, s->t + s->h, s->y_new, s->f_stage, s->dim,
217 s->matrix_data, s->rhs_sum, s->y_pert, s->f_pert,
218 &s->out_result->num_rhs_evals, &callback_failed});
219 if (st != LMMC_STATUS_OK) {
221 callback_failed ? LMMC_ODE_FAILURE_RHS_EVAL_FAILED
223 return st;
224 }
225 for (i = 0; i < s->dim; ++i) {
226 for (j = 0; j < s->dim; ++j) {
227 s->matrix.data[i * s->dim + j] = -s->h * sdirk_gamma * s->matrix_data[i * s->dim + j];
228 }
229 s->matrix.data[i * s->dim + i] += 1.0;
230 }
231 st = sdirk_linear_solve(&s->matrix, s->pivots, s->error, s->smoothed, s->dim);
232 if (st != LMMC_STATUS_OK) { return st; }
233 st = lmmc_ode_weighted_rms(s->smoothed, s->y, s->y_new, s->dim, s->local_cfg.abs_tol,
234 s->local_cfg.rel_tol, error_norm);
235 if (st != LMMC_STATUS_OK) { return st; }
236 return LMMC_STATUS_OK;
237}
238
239static lmmc_status_t sdirk_integrate(ode_sdirk_state_t* s, lmmc_real_t t_end) {
240 size_t attempts = 0;
241 lmmc_status_t st;
242 while (s->t < t_end && attempts < s->local_cfg.max_steps) {
243 lmmc_real_t error_norm = 0.0;
244 ++attempts;
245 if (s->h > t_end - s->t) s->h = t_end - s->t;
246 for (size_t stage = 0; stage < SDIRK_STAGES; ++stage) {
247 st = sdirk_solve_stage(s, stage);
248 if (st != LMMC_STATUS_OK) { return st; }
249 }
250 st = sdirk_estimate_error(s, &error_norm);
251 if (st != LMMC_STATUS_OK) { return st; }
252 if (error_norm <= 1.0) {
253 memcpy(s->y, s->y_new, s->work_bytes);
254 s->t += s->h;
255 s->out_result->num_steps += 1;
256 s->out_result->final_t = s->t;
257 lmmc_ode_do_log(&s->local_cfg, s->out_result->num_steps, s->t, s->y, s->dim);
258 } else if (s->h <= s->local_cfg.min_step) {
261 return st;
262 }
263 s->h = lmmc_ode_next_step(s->h, error_norm, &s->local_cfg);
264 }
265
266 if (s->t >= t_end) {
267 s->out_result->converged = 1;
269 st = LMMC_STATUS_OK;
270 } else {
273 }
274 return st;
275}
276
277static lmmc_status_t sdirk_cleanup(ode_sdirk_state_t* s, lmmc_status_t st) {
278 if (st != LMMC_STATUS_OK && s->out_result != NULL &&
281 }
285 return st;
286}
287
289 lmmc_ode_rhs_t rhs,
290 void* user_data,
291 size_t dim,
292 lmmc_real_t t_start,
293 lmmc_real_t t_end,
294 lmmc_real_t* y,
295 const lmmc_ode_config_t* cfg,
296 lmmc_ode_result_t* out_result
297) {
298 ode_sdirk_state_t state = {0};
299 ode_sdirk_state_t* s = &state;
300 size_t vectors_bytes = 0, matrix_count = 0, matrix_bytes = 0, pivot_bytes = 0;
301 size_t stage;
302 lmmc_status_t st;
303 s->rhs = rhs;
304 s->user_data = user_data;
305 s->dim = dim;
306 s->y = y;
307 s->out_result = out_result;
308 s->t = t_start;
309 st = validate_and_init_ode_config(s->rhs, s->dim, t_start, t_end, s->y, cfg,
310 &s->local_cfg, s->out_result, &s->work_bytes);
311 if (st != LMMC_STATUS_OK) { return st; }
312 if (!lmmc_safe_mul_size(s->work_bytes, SDIRK_VECTORS, &vectors_bytes) ||
313 !lmmc_safe_mul_size(s->dim, s->dim, &matrix_count) ||
314 !lmmc_safe_mul_size(matrix_count, sizeof(lmmc_real_t), &matrix_bytes) ||
315 !lmmc_safe_mul_size(s->dim, sizeof(size_t), &pivot_bytes)) {
318 }
319
320 s->work = (lmmc_real_t*)lmmc_memory_alloc(vectors_bytes);
321 s->matrix_data = (lmmc_real_t*)lmmc_memory_alloc(matrix_bytes);
322 s->pivots = (size_t*)lmmc_memory_alloc(pivot_bytes);
323 if (s->work == NULL || s->matrix_data == NULL || s->pivots == NULL) {
325 return sdirk_cleanup(s, st);
326 }
327 for (stage = 0; stage < SDIRK_STAGES; ++stage) s->k[stage] = s->work + stage * s->dim;
328 s->rhs_sum = s->work + 5 * s->dim;
329 s->y_stage = s->work + 6 * s->dim;
330 s->f_stage = s->work + 7 * s->dim;
331 s->y_pert = s->work + 8 * s->dim;
332 s->f_pert = s->work + 9 * s->dim;
333 s->residual = s->work + 10 * s->dim;
334 s->delta = s->work + 11 * s->dim;
335 s->y_new = s->work + 12 * s->dim;
336 s->y_hat = s->work + 13 * s->dim;
337 s->error = s->work + 14 * s->dim;
338 s->smoothed = s->work + 15 * s->dim;
339 s->matrix = (lmmc_mat_t){s->dim, s->dim, s->dim, s->matrix_data, 0};
340
341 s->h = lmmc_clamp(s->local_cfg.initial_step, s->local_cfg.min_step, s->local_cfg.max_step);
342 if (s->h > t_end - t_start) s->h = t_end - t_start;
343 lmmc_ode_do_log(&s->local_cfg, 0, s->t, s->y, s->dim);
344 st = sdirk_integrate(s, t_end);
345 return sdirk_cleanup(s, st);
346}
double lmmc_real_t
Public and internal LMMC real type.
lmmc_real_t * data
行优先存储的稠密矩阵。
一维稠密向量。
LMMC 内部使用的公共工具(仅源文件可见).
稠密矩阵直接分解接口(LU、Cholesky、QR)。
lmmc_status_t lmmc_lu_solve(const lmmc_mat_t *lu, const size_t *pivots, const lmmc_vec_t *b, lmmc_vec_t *x)
利用 LU 分解结果求解线性系统 A*x = b。
lmmc_status_t lmmc_lu_decompose_inplace(lmmc_mat_t *a, size_t *pivots, size_t *out_swap_count)
就地 LU 分解(带部分主元选取):P*A = L*U。
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_INVALID_DIMENSION
定义 ode.h:37
@ 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_real_t rel_tol
定义 ode.h:96
lmmc_ode_failure_t failure_reason
定义 ode.h:51
lmmc_real_t max_step
定义 ode.h:94
lmmc_ode_jac_t jacobian
定义 ode.h:100
lmmc_real_t min_step
定义 ode.h:93
lmmc_status_t lmmc_ode_sdirk4_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)
4 阶 SDIRK 求解器,采用嵌入误差估计和自适应步长。 非线性阶段按 abs_tol/rel_tol、当前及阶段状态计算 h*残差与 h*修正的加权 RMS。 迭代耗尽时保留最后接受的 y、fina...
ODE 求解配置。
定义 ode.h:91
ODE 求解结果统计。
定义 ode.h:46
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 lmmc_ode_jacobian_eval(const lmmc_ode_jacobian_request_t *request)
lmmc_real_t lmmc_ode_next_step(lmmc_real_t h, lmmc_real_t error_norm, const lmmc_ode_config_t *cfg)
lmmc_status_t lmmc_ode_values_are_finite(const lmmc_real_t *values, size_t count)
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)
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_ode_weighted_rms(const lmmc_real_t *error, const lmmc_real_t *y_old, const lmmc_real_t *y_new, size_t dim, lmmc_real_t abs_tol, lmmc_real_t rel_tol, lmmc_real_t *out_norm)
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_CONVERGENCE_FAILED
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT