sokobo
Loading...
Searching...
No Matches
differential_equations.cpp
1#include "include/differential_equations.h"
2#include "include/expression.h"
3#define _USE_MATH_DEFINES
4#include <cmath>
5#include <iostream>
6#include <algorithm>
7#include <stdexcept>
8#include <iostream>
9
10// First-order ODE: Euler's method
11std::vector<double> DifferentialEquations::eulerMethod(std::function<double(double, double)> f,
12 double x0, double y0, double h, int steps) {
13 if (steps <= 0) throw std::invalid_argument("Number of steps must be positive");
14
15 std::vector<double> solution;
16 solution.reserve(steps + 1);
17 solution.push_back(y0);
18
19 double x = x0;
20 double y = y0;
21
22 for (int i = 0; i < steps; i++) {
23 y = y + h * f(x, y);
24 x = x0 + (i + 1) * h;
25 solution.push_back(y);
26 }
27
28 return solution;
29}
30
31// First-order ODE: 4th-order Runge-Kutta method
32std::vector<double> DifferentialEquations::rungeKutta4(std::function<double(double, double)> f,
33 double x0, double y0, double h, int steps) {
34 if (steps <= 0) throw std::invalid_argument("Number of steps must be positive");
35
36 std::vector<double> solution;
37 solution.reserve(steps + 1);
38 solution.push_back(y0);
39
40 double x = x0;
41 double y = y0;
42
43 for (int i = 0; i < steps; i++) {
44 double k1 = h * f(x, y);
45 double k2 = h * f(x + h/2, y + k1/2);
46 double k3 = h * f(x + h/2, y + k2/2);
47 double k4 = h * f(x + h, y + k3);
48
49 y = y + (k1 + 2*k2 + 2*k3 + k4) / 6.0;
50 x = x0 + (i + 1) * h;
51 solution.push_back(y);
52 }
53
54 return solution;
55}
56
57// Adams-Bashforth method (4th order)
58std::vector<double> DifferentialEquations::adamsBashforth(std::function<double(double, double)> f,
59 double x0, double y0, double h, int steps) {
60 if (steps <= 0) throw std::invalid_argument("Number of steps must be positive");
61
62 std::vector<double> solution;
63 solution.reserve(steps + 1);
64 solution.push_back(y0);
65
66 // Use Runge-Kutta to get first few points
67 std::vector<double> f_values;
68 double x = x0;
69 double y = y0;
70
71 f_values.push_back(f(x, y));
72
73 // Get first 3 points using RK4
74 for (int i = 0; i < std::min(3, steps); i++) {
75 double k1 = h * f(x, y);
76 double k2 = h * f(x + h/2, y + k1/2);
77 double k3 = h * f(x + h/2, y + k2/2);
78 double k4 = h * f(x + h, y + k3);
79
80 y = y + (k1 + 2*k2 + 2*k3 + k4) / 6.0;
81 x = x0 + (i + 1) * h;
82 solution.push_back(y);
83 f_values.push_back(f(x, y));
84 }
85
86 // Continue with Adams-Bashforth
87 for (int i = 3; i < steps; i++) {
88 // 4th order Adams-Bashforth formula
89 double y_new = y + h * (55.0*f_values[i] - 59.0*f_values[i-1] +
90 37.0*f_values[i-2] - 9.0*f_values[i-3]) / 24.0;
91
92 x = x0 + (i + 1) * h;
93 y = y_new;
94 solution.push_back(y);
95 f_values.push_back(f(x, y));
96 }
97
98 return solution;
99}
100
101// System of ODEs using Runge-Kutta 4th order
102std::vector<std::vector<double>> DifferentialEquations::systemRungeKutta4(
103 std::vector<std::function<double(double, const std::vector<double>&)>> f,
104 double x0, const std::vector<double>& y0, double h, int steps) {
105
106 if (steps <= 0) throw std::invalid_argument("Number of steps must be positive");
107 if (f.size() != y0.size()) throw std::invalid_argument("Number of functions must match initial conditions");
108
109 int n = f.size();
110 std::vector<std::vector<double>> solution(n);
111
112 for (int i = 0; i < n; i++) {
113 solution[i].reserve(steps + 1);
114 solution[i].push_back(y0[i]);
115 }
116
117 double x = x0;
118 std::vector<double> y = y0;
119
120 for (int step = 0; step < steps; step++) {
121 std::vector<double> k1(n), k2(n), k3(n), k4(n);
122 std::vector<double> temp(n);
123
124 // Calculate k1
125 for (int i = 0; i < n; i++) {
126 k1[i] = h * f[i](x, y);
127 }
128
129 // Calculate k2
130 for (int i = 0; i < n; i++) {
131 temp[i] = y[i] + k1[i]/2;
132 }
133 for (int i = 0; i < n; i++) {
134 k2[i] = h * f[i](x + h/2, temp);
135 }
136
137 // Calculate k3
138 for (int i = 0; i < n; i++) {
139 temp[i] = y[i] + k2[i]/2;
140 }
141 for (int i = 0; i < n; i++) {
142 k3[i] = h * f[i](x + h/2, temp);
143 }
144
145 // Calculate k4
146 for (int i = 0; i < n; i++) {
147 temp[i] = y[i] + k3[i];
148 }
149 for (int i = 0; i < n; i++) {
150 k4[i] = h * f[i](x + h, temp);
151 }
152
153 // Update solution
154 for (int i = 0; i < n; i++) {
155 y[i] = y[i] + (k1[i] + 2*k2[i] + 2*k3[i] + k4[i]) / 6.0;
156 solution[i].push_back(y[i]);
157 }
158
159 x = x0 + (step + 1) * h;
160 }
161
162 return solution;
163}
164
165// Shooting method for boundary value problems
166std::vector<double> DifferentialEquations::shootingMethod(
167 std::function<double(double, double, double)> f,
168 double a, double b, double alpha, double beta, int n) {
169
170 if (n <= 0) throw std::invalid_argument("Number of points must be positive");
171
172 double h = (b - a) / n;
173
174 // Define the system for shooting method
175 auto system = [f](double x, const std::vector<double>& y) -> std::vector<double> {
176 return {y[1], f(x, y[0], y[1])};
177 };
178
179 // Shooting method using secant method to find correct initial slope
180 double s1 = 0.0; // First guess for initial slope
181 double s2 = 1.0; // Second guess for initial slope
182
183 auto shoot = [&](double slope) -> double {
184 std::vector<double> initial = {alpha, slope};
185 std::vector<std::function<double(double, const std::vector<double>&)>> funcs = {
186 [](double x, const std::vector<double>& y) { return y[1]; },
187 [f](double x, const std::vector<double>& y) { return f(x, y[0], y[1]); }
188 };
189
190 auto result = systemRungeKutta4(funcs, a, initial, h, n);
191 return result[0].back() - beta; // Error at right boundary
192 };
193
194 double f1 = shoot(s1);
195 double f2 = shoot(s2);
196
197 // Secant method iterations
198 for (int iter = 0; iter < 50; iter++) {
199 if (std::abs(f2) < 1e-10) break;
200
201 double s_new = s2 - f2 * (s2 - s1) / (f2 - f1);
202 s1 = s2;
203 f1 = f2;
204 s2 = s_new;
205 f2 = shoot(s2);
206 }
207
208 // Final solution with correct initial slope
209 std::vector<double> initial = {alpha, s2};
210 std::vector<std::function<double(double, const std::vector<double>&)>> funcs = {
211 [](double x, const std::vector<double>& y) { return y[1]; },
212 [f](double x, const std::vector<double>& y) { return f(x, y[0], y[1]); }
213 };
214
215 auto result = systemRungeKutta4(funcs, a, initial, h, n);
216 return result[0];
217}
218
219// 1D Heat equation using finite differences
220std::vector<std::vector<double>> DifferentialEquations::heatEquation1D(
221 double alpha, double L, double T, int nx, int nt,
222 std::function<double(double)> initialCondition,
223 std::function<double(double)> boundaryLeft,
224 std::function<double(double)> boundaryRight) {
225
226 if (nx <= 0 || nt <= 0) throw std::invalid_argument("Grid dimensions must be positive");
227
228 double dx = L / (nx - 1);
229 double dt = T / (nt - 1);
230 double r = alpha * dt / (dx * dx);
231
232 // Stability check for explicit method
233 if (r > 0.5) {
234 throw std::runtime_error("Unstable parameters: r = α*dt/dx² must be ≤ 0.5");
235 }
236
237 std::vector<std::vector<double>> u(nt, std::vector<double>(nx));
238
239 // Initial conditions
240 for (int i = 0; i < nx; i++) {
241 double x = i * dx;
242 u[0][i] = initialCondition(x);
243 }
244
245 // Time stepping
246 for (int t = 1; t < nt; t++) {
247 double time = t * dt;
248
249 // Boundary conditions
250 u[t][0] = boundaryLeft(time);
251 u[t][nx-1] = boundaryRight(time);
252
253 // Interior points using explicit finite difference
254 for (int i = 1; i < nx - 1; i++) {
255 u[t][i] = u[t-1][i] + r * (u[t-1][i+1] - 2*u[t-1][i] + u[t-1][i-1]);
256 }
257 }
258
259 return u;
260}
261
262// 1D Wave equation using finite differences
263std::vector<std::vector<double>> DifferentialEquations::waveEquation1D(
264 double c, double L, double T, int nx, int nt,
265 std::function<double(double)> initialPosition,
266 std::function<double(double)> initialVelocity) {
267
268 if (nx <= 0 || nt <= 0) throw std::invalid_argument("Grid dimensions must be positive");
269
270 double dx = L / (nx - 1);
271 double dt = T / (nt - 1);
272 double r = c * dt / dx;
273
274 // Stability check (CFL condition)
275 if (r > 1.0) {
276 throw std::runtime_error("Unstable parameters: c*dt/dx must be ≤ 1");
277 }
278
279 std::vector<std::vector<double>> u(nt, std::vector<double>(nx));
280
281 // Initial position
282 for (int i = 0; i < nx; i++) {
283 double x = i * dx;
284 u[0][i] = initialPosition(x);
285 }
286
287 // First time step using initial velocity
288 for (int i = 1; i < nx - 1; i++) {
289 double x = i * dx;
290 u[1][i] = u[0][i] + dt * initialVelocity(x) +
291 0.5 * r * r * (u[0][i+1] - 2*u[0][i] + u[0][i-1]);
292 }
293
294 // Boundary conditions (fixed ends)
295 for (int t = 0; t < nt; t++) {
296 u[t][0] = 0.0;
297 u[t][nx-1] = 0.0;
298 }
299
300 // Time stepping for t ≥ 2
301 for (int t = 2; t < nt; t++) {
302 for (int i = 1; i < nx - 1; i++) {
303 u[t][i] = 2*u[t-1][i] - u[t-2][i] +
304 r*r * (u[t-1][i+1] - 2*u[t-1][i] + u[t-1][i-1]);
305 }
306 }
307
308 return u;
309}
310
311// 2D Laplace equation using iterative method (Gauss-Seidel)
312std::vector<std::vector<double>> DifferentialEquations::laplaceEquation2D(
313 int nx, int ny, double tolerance,
314 std::function<double(double, double)> boundaryCondition) {
315
316 if (nx <= 0 || ny <= 0) throw std::invalid_argument("Grid dimensions must be positive");
317
318 std::vector<std::vector<double>> u(ny, std::vector<double>(nx, 0.0));
319 std::vector<std::vector<double>> u_old(ny, std::vector<double>(nx, 0.0));
320
321 // Set boundary conditions if provided
322 if (boundaryCondition) {
323 for (int i = 0; i < nx; i++) {
324 double x = static_cast<double>(i) / (nx - 1);
325 u[0][i] = boundaryCondition(x, 0.0); // Bottom
326 u[ny-1][i] = boundaryCondition(x, 1.0); // Top
327 }
328 for (int j = 0; j < ny; j++) {
329 double y = static_cast<double>(j) / (ny - 1);
330 u[j][0] = boundaryCondition(0.0, y); // Left
331 u[j][nx-1] = boundaryCondition(1.0, y); // Right
332 }
333 }
334
335 // Iterative solution using Gauss-Seidel method
336 int max_iterations = 10000;
337 for (int iter = 0; iter < max_iterations; iter++) {
338 u_old = u;
339
340 // Update interior points
341 for (int j = 1; j < ny - 1; j++) {
342 for (int i = 1; i < nx - 1; i++) {
343 u[j][i] = 0.25 * (u[j][i+1] + u[j][i-1] + u[j+1][i] + u[j-1][i]);
344 }
345 }
346
347 // Check convergence
348 double max_error = 0.0;
349 for (int j = 1; j < ny - 1; j++) {
350 for (int i = 1; i < nx - 1; i++) {
351 max_error = std::max(max_error, std::abs(u[j][i] - u_old[j][i]));
352 }
353 }
354
355 if (max_error < tolerance) {
356 break;
357 }
358 }
359
360 return u;
361}
362
363// Symbolic solution for linear ODEs (placeholder)
364std::shared_ptr<Expression> DifferentialEquations::solveLinearODE(const std::vector<double>& coeffs,
365 std::shared_ptr<Expression> rhs) {
366 // This would require full symbolic computation capabilities
367 // For now, return placeholder indicating symbolic solution not implemented
368 throw std::runtime_error("Symbolic ODE solution not implemented - requires complete expression tree system");
369}
370
371// Symbolic solution for separable ODEs (placeholder)
372std::shared_ptr<Expression> DifferentialEquations::solveSeparableODE(std::shared_ptr<Expression> expr) {
373 // This would require symbolic integration and algebraic manipulation
374 // For now, return placeholder indicating symbolic solution not implemented
375 throw std::runtime_error("Symbolic separable ODE solution not implemented - requires complete expression tree system");
376}