File size: 5,075 Bytes
279610a
 
 
 
 
 
 
 
3dd583e
 
279610a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dd583e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d283c3
 
 
 
 
 
 
 
 
 
 
 
 
3dd583e
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
Solves a single second-order ordinary differential equation of the form
d²y/dt² = f(t, y, dy/dt) with initial conditions y(t0)=y0 and dy/dt(t0)=dy0_dt.
"""
import numpy as np
from scipy.integrate import solve_ivp
from typing import Callable, List, Tuple, Dict, Any, Union
import matplotlib.pyplot as plt
from maths.differential_equations.ode_interface_utils import parse_float_list, parse_time_span, string_to_ode_func
import gradio as gr

def solve_second_order_ode(
    ode_func_second_order: Callable[[float, float, float], float],
    t_span: Tuple[float, float],
    y0: float,
    dy0_dt: float,
    t_eval_count: int = 100,
    method: str = 'RK45',
    **kwargs: Any
) -> Dict[str, Union[np.ndarray, str, bool]]:
    # ...existing code...
    def system_func(t: float, z: np.ndarray) -> List[float]:
        y_val, dy_dt_val = z[0], z[1]
        d2y_dt2_val = ode_func_second_order(t, y_val, dy_dt_val)
        return [dy_dt_val, d2y_dt2_val]

    initial_conditions_system = [y0, dy0_dt]

    try:
        t_eval = np.linspace(t_span[0], t_span[1], t_eval_count)
        sol = solve_ivp(system_func, t_span, initial_conditions_system, method=method, t_eval=t_eval, **kwargs)

        plot_path = None
        if sol.success:
            try:
                plt.figure(figsize=(12, 7))

                plt.subplot(2,1,1)
                plt.plot(sol.t, sol.y[0], label=f'y(t), y0={y0}')
                plt.xlabel("Time (t)")
                plt.ylabel("y(t)")
                plt.title(f"Solution of Second-Order ODE: y(t) ({method})")
                plt.legend()
                plt.grid(True)

                plt.subplot(2,1,2)
                plt.plot(sol.t, sol.y[1], label=f'dy/dt(t), dy0/dt={dy0_dt}', color='orange')
                plt.xlabel("Time (t)")
                plt.ylabel("dy/dt(t)")
                plt.title(f"Solution of Second-Order ODE: dy/dt(t) ({method})")
                plt.legend()
                plt.grid(True)

                plt.tight_layout()
                plot_path = "ode_second_order_solution_plot.png"
                plt.savefig(plot_path)
                plt.close()
            except Exception as e_plot:
                print(f"Warning: Could not generate plot: {e_plot}")
                plot_path = None

        return {
            't': sol.t,
            'y': sol.y[0],      # First component of the system's solution
            'dy_dt': sol.y[1],  # Second component of the system's solution
            'message': sol.message,
            'success': sol.success,
            'plot_path': plot_path
        }
    except Exception as e:
        return {
            't': np.array([]),
            'y': np.array([]),
            'dy_dt': np.array([]),
            'message': f"Error during ODE solving: {str(e)}",
            'success': False,
            'plot_path': None
        }

# --- Gradio Interface for Second-Order ODEs ---
second_order_ode_interface = gr.Interface(
    fn=lambda ode_str, t_span_str, y0_val_str, dy0_dt_val_str, t_eval_count, method: solve_second_order_ode(
        string_to_ode_func(ode_str, ('t', 'y', 'dy_dt')),
        parse_time_span(t_span_str),
        parse_float_list(y0_val_str, expected_len=1)[0],
        parse_float_list(dy0_dt_val_str, expected_len=1)[0],
        int(t_eval_count),
        method
    ),
    inputs=[
        gr.Textbox(label="ODE Function (lambda t, y, dy_dt: ...)",
                   placeholder="e.g., lambda t, y, dy_dt: -0.1*dy_dt - math.sin(y)",
                   info="Define d²y/dt². `y` is current value, `dy_dt` is current first derivative."),
        gr.Textbox(label="Time Span (t_start, t_end)", placeholder="e.g., 0,20"),
        gr.Textbox(label="Initial y(t_start)", placeholder="e.g., 1.0"),
        gr.Textbox(label="Initial dy/dt(t_start)", placeholder="e.g., 0.0"),
        gr.Slider(minimum=10, maximum=1000, value=100, step=10, label="Evaluation Points Count"),
        gr.Radio(choices=['RK45', 'LSODA', 'BDF', 'RK23', 'DOP853'], value='RK45', label="Solver Method")
    ],
    outputs=[
        gr.Image(label="Solution Plot (y(t) and dy/dt(t))", type="filepath", show_label=True, visible=lambda res: res['success'] and res['plot_path'] is not None),
        gr.Textbox(label="Solver Message"),
        gr.Textbox(label="Success Status"),
        gr.JSON(label="Raw Data (t, y, dy_dt values)", visible=lambda res: res['success'])
    ],
    title="Second-Order ODE Solver",
    description="""
        Solves d²y/dt² = f(t, y, dy/dt) for a single second-order ODE.

        - Enter a Python lambda for the ODE (e.g., `lambda t, y, dy_dt: -0.1*dy_dt - math.sin(y)`).
        - Initial y(t_start) and dy/dt(t_start) must be single values.

        **Examples:**

        - Damped oscillator: `lambda t, y, dy_dt: -0.1*dy_dt - y`, y0: `1.0`, dy0/dt: `0.0`, t_span: `0,20`
        - Simple pendulum: `lambda t, y, dy_dt: -9.81/1.0 * math.sin(y)`, y0: `math.pi/4`, dy0/dt: `0`, t_span: `0,10`

        WARNING: Uses eval() for the ODE function string - potential security risk.
        """,
    flagging_mode="manual"
)