110 KiB
110 KiB
In [5]:
import numpy as np
import matplotlib.pyplot as plt
# ===================== First Task Functions ===================== #
# Equations for the first task
def func_a(x, y):
return x + np.cos(y)
def func_b(x, y):
return x**2 + y**2
# Runge-Kutta 2nd order method for first-order ODE
def runge_kutta_2(f, x0, y0, h, N):
x = x0 + np.arange(N+1) * h
y = np.zeros(N+1)
y[0] = y0
for k in range(N):
k1 = f(x[k], y[k])
k2 = f(x[k] + h, y[k] + h * k1)
y[k+1] = y[k] + h * (k1 + k2) / 2
return x, y
# Runge-Kutta 4th order method for first-order ODE
def runge_kutta_4(f, x0, y0, h, N):
x = x0 + np.arange(N+1) * h
y = np.zeros(N+1)
y[0] = y0
for k in range(N):
k1 = f(x[k], y[k])
k2 = f(x[k] + h/2, y[k] + h * k1 / 2)
k3 = f(x[k] + h/2, y[k] + h * k2 / 2)
k4 = f(x[k] + h, y[k] + h * k3)
y[k+1] = y[k] + h * (k1 + 2*k2 + 2*k3 + k4) / 6
return x, y
# ===================== Second Task Functions ===================== #
# Converts second-order ODE to a system of first-order ODEs
def second_order_to_system(x, y, dy):
return dy, y * np.sin(x)
# Runge-Kutta 2nd order method for second-order ODE
def runge_kutta_2_second_order(f, x0, y0, dy0, h, N):
x = x0 + np.arange(N+1) * h
y = np.zeros(N+1)
dy = np.zeros(N+1)
y[0] = y0
dy[0] = dy0
for k in range(N):
k1_y, k1_dy = f(x[k], y[k], dy[k])
k2_y, k2_dy = f(x[k] + h, y[k] + h * k1_y, dy[k] + h * k1_dy)
y[k+1] = y[k] + h * (k1_y + k2_y) / 2
dy[k+1] = dy[k] + h * (k1_dy + k2_dy) / 2
return x, y
# Runge-Kutta 4th order method for second-order ODE
def runge_kutta_4_second_order(f, x0, y0, dy0, h, N):
x = x0 + np.arange(N+1) * h
y = np.zeros(N+1)
dy = np.zeros(N+1)
y[0] = y0
dy[0] = dy0
for k in range(N):
k1_y, k1_dy = f(x[k], y[k], dy[k])
k2_y, k2_dy = f(x[k] + h/2, y[k] + h * k1_y / 2, dy[k] + h * k1_dy / 2)
k3_y, k3_dy = f(x[k] + h/2, y[k] + h * k2_y / 2, dy[k] + h * k2_dy / 2)
k4_y, k4_dy = f(x[k] + h, y[k] + h * k3_y, dy[k] + h * k3_dy)
y[k+1] = y[k] + h * (k1_y + 2*k2_y + 2*k3_y + k4_y) / 6
dy[k+1] = dy[k] + h * (k1_dy + 2*k2_dy + 2*k3_dy + k4_dy) / 6
return x, y
# ===================== Main Code for Both Tasks ===================== #
# Parameters for both tasks
methods = {
'RK2': {'first_order': runge_kutta_2, 'second_order': runge_kutta_2_second_order},
'RK4': {'first_order': runge_kutta_4, 'second_order': runge_kutta_4_second_order}
}
h_values = [0.1, 0.05, 0.01, 0.005, 0.001]
N_values = [int((2 - 1)/h) for h in h_values] # For the first task with x from 1 to 2
# First Task Equations
equations_first = {
'a': {'func': func_a, 'x0': 1.0, 'y0': 30.0, 'x_end': 2.0},
'b': {'func': func_b, 'x0': 2.0, 'y0': 1.0, 'x_end': 1.0} # Reverse integration
}
# Second Task Parameters
x0_second = 0.0
y0_second = 0.0
dy0_second = 1.0
x_end_second = 1.0
N_values_second = [int((x_end_second - x0_second)/h) for h in h_values]
# Function to perform calculations and plotting for a given task
def solve_task(task_label, equations, N_values_task, x0_task, x_end_task, is_second_order=False):
for eq_label, eq_params in equations.items():
print(f"\nSolution for equation {task_label}{eq_label}:")
x0 = eq_params['x0']
y0 = eq_params['y0']
if is_second_order:
dy0 = eq_params['dy0']
x_end = eq_params['x_end']
if x_end < x0:
direction = -1
else:
direction = 1
for method_label, method_funcs in methods.items():
method_func = method_funcs['second_order'] if is_second_order else method_funcs['first_order']
print(f"\nMethod {method_label}:")
ys = {}
xs = {}
abs_errors = []
max_abs_errors = []
for h, N in zip(h_values, N_values_task):
h = direction * h # Account for integration direction
if is_second_order:
x, y = method_func(second_order_to_system, x0, y0, dy0, h, N)
else:
x, y = method_func(eq_params['func'], x0, y0, h, N)
xs[N] = x
ys[N] = y
for i in range(1, len(N_values_task)):
N_prev = N_values_task[i-1]
N_curr = N_values_task[i]
# Find common indices for comparison
factor = N_curr // N_prev
indices_prev = [1, N_prev]
indices_curr = [k * factor for k in indices_prev]
# Compute relative errors
rel_errors = np.abs(ys[N_curr][indices_curr] - ys[N_prev][indices_prev])
print(f"N_{N_curr}: {' '.join(map(str, rel_errors))}")
# Compute maximum absolute errors
max_error = np.max(rel_errors)
max_abs_errors.append(max_error)
# Plotting log2 of absolute errors
plt.plot(np.log2(N_values_task[1:]), np.log2(max_abs_errors), label=f"{method_label} for equation {task_label}{eq_label}")
plt.xlabel("log2(N)")
plt.ylabel("log2(max absolute error)")
plt.legend()
plt.title(f"Logarithm of absolute errors for task {task_label}")
plt.show()
# ===================== Solve First Task ===================== #
solve_task('1', equations_first, N_values, x0_task=None, x_end_task=None)
# ===================== Solve Second Task ===================== #
# Second Task Equation
equations_second = {
'': {'dy0': dy0_second, 'x0': x0_second, 'y0': y0_second, 'x_end': x_end_second}
}
solve_task('2', equations_second, N_values_second, x0_task=x0_second, x_end_task=x_end_second, is_second_order=True)
Solution for equation 1a: Method RK2: N_20: 0.0002785283564428198 0.0038035441958399474 N_100: 4.479515251887278e-05 0.0012767268215512217 N_200: 2.7750003539495083e-07 4.103739219374347e-05 N_1000: 4.440127909788316e-08 1.3191150621594261e-05 Method RK4: N_20: 2.9636773035690567e-07 3.9579403434686355e-06 N_100: 9.184862648226044e-09 2.6419341025984977e-07 N_200: 2.6076918402395677e-12 3.9756997693984886e-10 N_1000: 8.526512829121202e-14 2.646061147970613e-11 Solution for equation 1b: Method RK2: N_20: 0.0014339682258658337 0.019603229003013034 N_100: 9.465097127459021e-05 0.0035429668461870456 N_200: 5.85138142383812e-08 6.395910166601126e-05 N_1000: 2.542437982366863e-08 1.8243564207320873e-05 Method RK4: N_20: 1.6409863435429273e-05 1.5852614062783488e-05 N_100: 5.942575315165399e-07 2.292905823431113e-06 N_200: 1.8908408172535474e-10 3.99405974960132e-09 N_1000: 6.314393452555578e-12 2.6547564146994773e-10
Solution for equation 2: Method RK2: N_20: 6.247396158842733e-06 0.0006487724632973091 N_100: 4.999227261395789e-07 0.00021289741698327092 N_200: 6.249973958510902e-10 6.760371213720973e-06 N_1000: 4.999992173765344e-11 2.169024081455362e-06 Method RK4: N_20: 1.778789573969597e-09 1.0195093080866968e-07 N_100: 3.0951276264179484e-11 6.3144536266435125e-09 N_200: 1.9359513991901167e-15 8.852918398360998e-12 N_1000: 3.382710778154774e-17 5.822009541134321e-13