Friday, 25 July 2025

A Shooting Method Approach to Boundary-Value ODEs

Topic: Differential Equations Boundary-Value Problems

Subject: Numerical Methods

Tool: Scilab

By: Gani Comia, July 2025

  • The Shooting Method

The shooting method is a numerical technique of solving boundary-value problems (BVPs) for second-order ordinary differential equations (ODE) of the form shown in Equation (1).

$$y''(x) = f(x, y, y') , \quad a \leq x \leq b \tag{1}$$

with boundary conditions

$$y(a) = \alpha , \quad y(b) = \beta \tag{2}$$

A sample ODE, along with the boundary conditions provided in Equations (3) and (4), will be solved to demonstrate the method in this article.

$$\frac{d^2 y}{dx^2} + e^{-x y} + \sin\left(\frac{dy}{dx}\right) = 0 , \quad 1 \leq x \leq 2 \tag{3}$$

$$y(1) = 0 , \quad y(2) = 0 \tag{4}$$

The idea behind the shooting method is to convert the BVP to the initial-value problem (IVP) as in Equation (5) and (6). The given 2nd-order ODE requires to be converted to the system of ODEs.

$$y'' = f(x, y, y') \Rightarrow \text{Sys of ODE} \left\{ \begin{aligned} y_1 &= y \\ y_2 &= y' \\ y_1^{'} &= y_2 \\ y_2^{'} &= f(x, y_1, y_2) \end{aligned} \right\} \tag{5}$$

$$y_1(a) = \alpha , \quad y_2(b) = s \tag{6}$$

Solving the converted IVP requires providing a guess for the value of \(s\) and solve the system numerically using Runge-Kutta method to get the value of the solution, \(y_{\beta} = y(b)\), at \(x = b\). If \(y_{\beta} \neq \beta\), adjust the guess, \(s\), so that \(y_{\beta} - \beta = 0\). For the given two guesses of \(s\), compute the best guess to make \(y_{\beta} - \beta = 0\) by employing the root-finding algorithm which is the Bisection method for this article.

For our given problem in Equation (3) and (4), two guesses value for \(s\) will be tried for \(y_2(a)\).

$$s_1 = 0.1 , \quad s_2 = \frac{\pi}{2} \tag{7}$$

Figure 1 is the resulting solution for the IVP using \(s_1\) and \(s_2\).

Figure 1. Solution to ODE IVP with \(y(a) = \alpha\) , \(s_1\) , and \(s_2\).

As shown in Figure 1, the two guesses, \(s_1\) and \(s_2\), do not meet the \(y(b) = \beta\) or \(y(2) = 0\) boundary conditions. Finding the value of \(s\) that makes \(y_{\beta} - \beta = 0\) requires defining a Residual function in Equation 8.

$$\phi(s) = y(b; s) - \beta \tag{8}$$

Residual function measures how far the solution with guesses is from satisfying the boundary condition at \(x = b\). For the given sample problem, \(\phi(s) = 1 \times 10^{-6}\) will be utilized in the root-finding algorithm, the Bisection method. Figure 2 is the visualization of the solution for sample ODE BVP.

Figure 2. Solution to ODE BVP using the Shooting Method.

The solution \(y(x)\) satisfies the boundary conditions \(y(a) = \alpha\) and \(y(b) = \beta\). Implementation of the Runge-Kutta method and the Bisection method is used in our Scilab script. The two guesses, \(s_1\) and \(s_2\), and the Residual function \(\phi(s)\) are used as parameters in the script.

  • Scilab Script for Figure 2

// Copyright (C) 2025 - I.S.Comia
// Date of creation: Jul 12, 2025
// Shooting Method in Solving 2nd-Order ODE BVP
clear;clc;clf;
// Bundary Value Problem
// y'' = -exp(-x.*y)-sin(y'), y(1) = 0, y(2) = 0
// y'' = f(x,y,y'), y(a) = alpha, y(b) = beta

// Subroutine
// y'' = -exp(-x.*y)-sin(y') in Scilab function
function dydx=ode_system(x, y)
    // y(1) = y, y(2) = y'
    dydx = [y(2); -exp(x.*y(1))-sin(y(2))];
endfunction

// solving the ODE using IVP with guessed initial slope
// using Runge-Kutta Method
function yb=solve_ivp(s, a, b, h)
    // Initial value for y and y'
    x = a:h:b;
    n = length(x);
    y = zeros(2, n);
    y(:, 1) = [0; s];  // y(a) = 0, y'(a) = s (Note: s - slope)
    // this is Runge-Kutta
    for i = 1:n-1
        k1 = h * ode_system(x(i), y(:, i));
        k2 = h * ode_system(x(i) + h/2, y(:, i) + k1/2);
        k3 = h * ode_system(x(i) + h/2, y(:, i) + k2/2);
        k4 = h * ode_system(x(i) + h, y(:, i) + k3);
        y(:, i+1) = y(:, i) + (k1 + 2*k2 + 2*k3 + k4)/6;
    end

    yb = y(1, $); // return y(b)
endfunction

// Main Function
// Parameters
a = 1;
b = 2;
alpha = 0;
beta = 0;
h = 0.05;

// Two Initial guesses for s = y'(a) or slopes, s @ x = a
s1 = 0.1;
s2 = %pi/2;
tol = 1e-6;

// Plot of IVP using s1 and s2
x = a:h:b
x0 = a
y1_0 = [0;s1]
y1 = ode(y1_0,x0,x,ode_system)
y2_0 = [0;s2]
y2 = ode(y2_0,x0,x,ode_system)

plot(x, y1(1, :), "g.-", "linewidth",1);
plot(x, y2(1, :), "g.-", "linewidth",1);

// Shooting Method with Bisection Method
while abs(s2 - s1) > tol
    smid = (s1 + s2)/2;
    yb1 = solve_ivp(s1, a, b, h);
    ybmid = solve_ivp(smid, a, b, h);
    
    if (yb1 - beta)*(ybmid - beta) < 0 then
        s2 = smid;
    else
        s1 = smid;
    end
end

// Final solution with best guess
s_final = (s1 + s2)/2;
x = a:h:b;
n = length(x);
y = zeros(2, n);
y(:, 1) = [alpha; s_final];

for i = 1:n-1
    k1 = h * ode_system(x(i), y(:, i));
    k2 = h * ode_system(x(i) + h/2, y(:, i) + k1/2);
    k3 = h * ode_system(x(i) + h/2, y(:, i) + k2/2);
    k4 = h * ode_system(x(i) + h, y(:, i) + k3);
    y(:, i+1) = y(:, i) + (k1 + 2*k2 + 2*k3 + k4)/6;
end

// Visualization of solution to ODE BVP
fig = gcf()
fig.figure_size = [700, 700]
plot(x, y(1, :), "b-", "linewidth",3);
plot(x, y(1, :), "ro", "linewidth",4.5);
title("$\text{Shooting Method Solution to ODE BVP}$","fontsize",4.5);
xlabel("$\LARGE x$")
ylabel("$\LARGE y(x)$")
note = ["$\Large y(x) \; \text{w/ IVPs}$";"$\Large y(x) \; \text{w/ BVP}$";" "]
legend(note,2,with_box=%F)
xgrid(color("grey"),1,7)
// Plot annotation
h1 = xstring(1.55,0.725,"https://gani-mech-toolbox.blogspot.com")
h1.font_size = 2
eqn1 = "$\frac{d^2 y}{dx^2}+e^{-xy}+\sin\left(\frac{dy}{dx}\right)=0\text{,}\;
y(1)=0\text{,}\;y(2)=0$"
h2 = xstring(1.05,-0.4,eqn1)
h2.font_size = 3.5
h3 = xstring(1.05,-0.25,"Given : 2nd-Order ODE BVP")
h3.font_size = 3
eqn2 = "$y(a) = \alpha, \quad \left.\frac{dy}{dx} \right|_{x=a} = s_1$"
h4 = xstring(1.6,-0.125,eqn2)
h4.font_size = 3.5
eqn3 = "$y(a) = \alpha, \quad \left.\frac{dy}{dx} \right|_{x=a} = s_2$"
h5 = xstring(1.5,0.55,eqn3)
h5.font_size = 3.5
eqn4 = "$y(a) = \alpha, \quad \left.\frac{dy}{dx} \right|_{x=a} \; \mid \; 
y_\beta - \beta = 0$"
h6 = xstring(1.5,0.15,eqn4)
h6.font_size = 3.5

ax = gca()
ax.data_bounds = [1 -0.4; 2 0.8]

  • Scilab Script for Figure 1

// Copyright (C) 2025 - I.S.Comia
// Date of creation: Jul 12, 2025
// Solution to 2nd-Order ODE IVP using Scilab's ode() function
clear;clc;clf;
// Initial Value Problem
// y'' = -exp(-x.*y)-sin(y'), y(1) = 0, y'(1) = s1 & s2
// y'' = f(x,y,y'), y(a) = alpha, y'(a) = s1 & s2

// Subroutine
// y'' = -exp(-x.*y)-sin(y') in Scilab function
function dydx=ode_system(x, y)
    // y(1) = y, y(2) = y'
    dydx = [y(2); -exp(x.*y(1))-sin(y(2))];
endfunction

// Main Function
// Parameters
a = 1;
b = 2;
alpha = 0;
h = 0.05;
// Two Initial guesses for s = y'(a) or slope s @ x = a
s1 = 0.1;
s2 = %pi/2;

// Plot of IVP using s1 and s2
x = a:h:b
x0 = a
y1_0 = [0;s1]
y1 = ode(y1_0,x0,x,ode_system)
y2_0 = [0;s2]
y2 = ode(y2_0,x0,x,ode_system)

// Visualization of solution to ODE IVP
fig = gcf()
fig.figure_size = [700, 700]
plot(x, y1(1, :), "g.-", "linewidth",1);
plot(x, y2(1, :), "m.-", "linewidth",1);
note = ["Solution to ODE IVP with ,","$y(a)=\alpha$",",","$\frac{dy}{dx}(a) = s$"]
title(note,"fontsize",3.5);
xlabel("$\LARGE x$")
ylabel("$\LARGE y(x)$")
note1 = ["$y(x) \; \text{w/ IVP} \; s_1 = 0.1$","$y(x) \; \text{w/ IVP} \; 
s_2 = \frac{\pi}{2}$"]
a1 = legend(note1,2,with_box=%F)
a1.font_size = 3
xgrid(color("grey"),1,7)
// Plot annotations
h1 = xstring(1.55,0.725,"https://gani-mech-toolbox.blogspot.com")
h1.font_size = 2
eqn1 = "$\frac{d^2 y}{dx^2}+e^{-xy}+\sin\left(\frac{dy}{dx}\right)=0\text{,}\;
y(1)=0\text{,}\;\frac{dy}{dx}(1)=s$"
h2 = xstring(1.05,-0.4,eqn1)
h2.font_size = 3.5
h3 = xstring(1.05,-0.25,"Given : 2nd-Order ODE IVP")
h3.font_size = 3
eqn2 = "$y(a) = \alpha, \quad \left.\frac{dy}{dx} \right|_{x=a} = s_1$"
h4 = xstring(1.6,-0.125,eqn2)
h4.font_size = 3.5
eqn3 = "$y(a) = \alpha, \quad \left.\frac{dy}{dx} \right|_{x=a} = s_2$"
h5 = xstring(1.5,0.55,eqn3)
h5.font_size = 3.5
// Plot limits
ax = gca()
ax.data_bounds = [1 -0.4; 2 0.8]

The shooting method is an effective approach for solving second-order boundary value problems (BVPs), particularly in mechanical, thermal, and structural engineering applications. To apply this method, the BVP is converted into an initial value problem (IVP) by estimating the necessary initial conditions. The IVP is then solved, and the results are compared with the boundary conditions at the opposite end. This process is repeated, adjusting the initial guesses as needed, until the solution satisfies the boundary conditions. While this approach involves some trial and error, linear second-order problems typically require no more than two iterations to converge.

Feel free to comment for inquiry, clarification, correction or suggestion for improvement. Drop your email to make a request to the author.

References

  1. Gerald, C. and Wheatly, P., Applied Numerical Analysis. Addison-Wesley Publishing Company, Inc. 1994.
  2. ChatGPT. AI Tools. Accessed on July 12, 2025.
  3. Google AI Overview. AI Tools. Accessed on July 12, 2025.

Wednesday, 9 July 2025

Exponential Illusions: Modeling Population Growth with Caution

Topic: Exponential Growth Model and Limitation

Subject: Numerical Methods

Tool: Scilab

By: Gani Comia, July 2025

  • Ordinary Differential Equation (ODE) for Exponential Growth

The ODE representing exponential growth is a fundamental model frequently used in real-world application of predictive modeling, particularly in demographics, ecology, and urban planning, to forecast future population sizes based on past trends and other factors.

The population growth is represented by the differential equation shown in Equation (1). The equation signifies that the rate of population change is directly proportional to the current population size.

$$\frac{dP}{dt}=r \, P \tag{1}$$

Where:
\(dP/dt\) – rate of change of the population with respect to time
\(P\) – population size at any given initial time
\(r\) – growth rate, an assumed positive constant that influences population growth

Equation (1) has a general solution as shown in Equation (2).

$$P(t) = P_0 \; e^{r \, t}, \quad P(0) = P_0 \tag{2}$$

Where:
\(P(t)\) – population at any given time, \(t\)
\(P_0\) – population at time, \(t = 0\)

The ODE representing the population growth model has the following limitations if applied in predictive modelling. First, it predicts unlimited and unrealistic growth over the long period of time, which is not sustainable in the long run due to factors resulting to limitation of resources. Second, the growth rate considered constant does not account for changes in birth and death rates or other environmental factors.

Below is the list of models or techniques for estimating or describing the population aside from exponential growth.

  1.  Exponential Growth
  2. Logistic Growth
  3. System Dynamics
  4. Naive Bayes and Decision Tree
  5. Grey Prediction

  • Numerical Solution to ODE using Euler's Method

The Scilab scripts used for calculations implements the Euler method to numerically approximate the solution of a first-order ordinary differential equation modeling exponential growth. Euler method requires an ODE and initial condition in the form of Equation (3).

$$\frac{dP}{dt} = f(t,P) , \quad (t_0 , P_0) \tag{3}$$

The approximate solution using Euler’s method is shown in Equation (4).

$$\frac{\Delta P}{\Delta t} \approx \frac{P_{n+1} - P_n}{h} \approx f(t, P) \tag{4}$$

Where:
\(h\) – step size, \(h = \Delta t\)

\(P_{n+1}\) is the solution to the ODE and can be solved by rearranging the Equation (4) leading to Equation (5).

$$P_{n+1} = P_n + h \; f(t, P) \tag{5}$$

The \(f(t,P)\) requires finding the growth rate, \(r\), for the two conditions as shown in Equation (6).

$$\large r = \frac{\ln \left( {\frac{P_t}{P_0}} \right)}{t} \tag{6}$$


  • Philippine Statistics Authority (PSA) Population Estimates

This article will use the exponential growth model to predict and estimates the population growth of the Philippines in comparison with a country’s agency (PSA) estimates. The agency is mandated to conduct national censuses and surveys including those on population.

Table 1 presents the population projections from the PSA up to the year 2055. These projections will be analyzed alongside the model based on exponential growth.

Table 1. Philippine Population Projection until 2055


  • PSA Estimates and Exponential Growth Comparison

Figure 1 illustrates the difference between two methods of estimation. The exponential growth model deviates from the PSA estimates after 25 years. Exponential growth models can accurately predict population trends during the initial growth phase, but their reliability decreases as other factors cause deviations from exponential behavior.

Figure 1. Population Growth Model and PSA Estimates


Below is the Scilab script to produce the results shown in Figure 1.

  • Scilab Script
// Copyright (C) 2025 - I.S.Comia
// Date of creation: Jul 6, 2025
// Predictive Modeling using Exponential Growth
clear;clc;

// Subroutine
// Population or Exponential Growth Model (1st-Order ODE)
function dPdt=f(t, P)
    global r;
    dPdt = r * P;
endfunction

function r=growthRate(P_0, P_t, t)
    // calculate the estimated growth rate
    // input:
    //      P_0 - quantity at t = 0
    //      P_t - quantity at a given time t
    //      t - time
    // output:
    //      r - estimated growth rate
    r = log(P_t./P_0)./t;
endfunction

// Main function
// Problem case scenario
// Phil. Statistic Authority (PSA) estimated population from year 2020 to 2050
// Primary parameters
actYear = [2020 2025 2030 2035 2040 2045 2050 2055];
// Population in millions
Pop = [109.2027 113.8631 118.8738 123.9636 128.8260 133.0245 136.2989 138.6727];
Yrs = actYear - 2020;                       // years covered
t = Yrs(2)-Yrs(1);
global r;
r = growthRate(Pop(1),Pop(2),t);            // growth rate estimate
T_initial = Yrs(1);                         // initial time
P_initial = Pop(1);                         // initial population (millions)

// Secondary parameters
T_final = Yrs($);                           // years, final time
dt = 1/12;                                  // month, step size,
N = T_final/dt;                             // number of steps

// Euler method solution to IVP ODE
// Initial conditions for time and P-value
T(1) = T_initial;
P(1) = P_initial;
// Solution
for k = 1:N
    dPdt = f(T(k), P(k));
    T(k+1) = T(k) + dt;
    P(k+1) = P(k) + dPdt * dt;
end

// Plotting of ODE solution
clf;
fig = gcf()
fig.figure_size = [700, 700]
plot(T,P,"b-","LineWidth",4)
title("Population Growth Model, Year 2020~2055","FontSize",4)
xlabel("Time (years)","FontSize",3.5)
ylabel("Population (millions)","FontSize",3.5)
xgrid(7,1)

// Population estimate for T_value~5 years or Year 2025
idT = max(find(T <= 5.05));
T_value = T(idT);
P_value = P(idT);
mprintf("For T_value: %4.2f, P_value: %6.3f \n", T_value, P_value)

// Plot of information for Year 2025
plot(T_value,P_value,"rs","MarkerFaceColor","r","MarkerSize",13)
ann_1=xstring(T_value+0.75,P_value-1,["Year:2025,Pop:",string(round(P_value)),"M"])
ann_1.font_size = 2
ann_2 = xstring(20,102.5,"https://gani-mech-toolbox.blogspot.com")
ann_2.font_size = 2
ann_3 = xstring(T_initial-2.5,P_initial-2.5,"Year: 2020")
ann_3.font_size = 2

T_line = [T_value T_value T_initial-5];
P_line = [100 P_value P_value];
plot(T_line,P_line,"r--","LineWidth",1)

// PSA population estimate
plot(Yrs,Pop,"ko","MarkerFaceColor","k","MarkerSize",8)
ann_4 = ["Exponential Growth Model";"Est Pop @ Year 2025";"";"PSA Pop Estimate"]
ann_5 = legend(ann_4,2,with_box=%F)
ann_5.font_size = 2

ax = gca()
ax.data_bounds = [-5 100; 40 150]

Feel free to comment for inquiry, clarification, correction or suggestion for improvement. Drop your email to make a request to the author.

References
  1. James, G., et al., Modern Engineering Mathematics. Addison-Wesley Publishing Company, Inc. 1993.
  2. “Philippine Population is Projected to be around 138.67 Million by 2055 under Scenario 2”. Philippine Statistics Authority. January 31, 2024. https://psa.gov.ph/content/philippine-population-projected-be-around-13867-million-2055-under-scenario-2
  3. ChatGPT. AI Tools. Accessed on July 6, 2025.
  4. Google AI Overview. AI Tools. Accessed on July 9, 2025. 

Sunday, 11 May 2025

C++ and Python for Numerical Solution to Spring-Mass-Damper Model

Topic: SDOF Spring-Mass-Damper System
Subject: Numerical Methods & Mechanical Vibration
Tools: C++, CodeBlocks, GitHub Copilot, LabPlot, Python, VS Code, and QCAD

By: Gani Comia, May 2025

  • Spring-Mass-Damper Model and Tools for Numerical Solution

Numerical solutions, such as Euler's method or Runge-Kutta method, can be used to determine the free response of a damped single-degree-of-freedom system. These frequently employed numerical techniques are utilized to solve vibration problems for which closed-form analytical solutions are not readily obtainable. Runge-Kutta simulations provide a higher degree of accuracy and quality compared to those generated by the Euler method.

C++ and Python programming language will be used in this article to solve numerically the response from a Spring-Mass-Damper Model using the Runge-Kutta method of 4th-Order. One may refer to an article in this blog entitled “Simple Harmonic Motion Solution using Runge-Kutta RK4” for the discussion of RK4 numerical method as applied to 2nd-Order ODE.

As observed, free oscillations in most systems eventually decay and the motion ceases entirely. This phenomenon can be modelled by adding a damper to the spring-mass model of the mechanical systems. The equation of motion of the spring-mass-damper model is represented by

$$m \, y''(t) + c \, y'(t) + k \, y(t) = 0  \tag{1}$$

Where:
\(m\) – mass, \(kg\)
\(c\) – damping coefficient, \(kg/s\)
\(k\) – spring constant, \(N/m\)
\(y’’(t)\) – acceleration as a function of time, \(m/s^2\)
\(y’(t)\) – velocity as a function of time, \(m/s\)
\(y(t)\) – displacement as a function of time, \(m\)

Subject to initial conditions

$$y(0) = y_0 \quad \text{and} \quad y'(0) = v_0  \tag{2}$$

Where:
\(y_0\) – initial position, \(m\), at \(t = 0 \; s\)
\(v_0\) – initial velocity, \(m/s\), at \(t = 0 \, s\)

Equation (1) is also referred to as a damped single-degree-of-freedom (SDOF) system. In analyzing the damped SDOF system, the non-dimensional number, \(\zeta\), called the damping ratio dictates the type of its response. Damping ratio is defined in Equation (3)

$$\zeta = \frac{c}{2 \, \sqrt{k \, m}}  \tag{3}$$

Damping ratio defines the types of motion of the response as follows

  1. Underdamped: \(0 < \zeta < 1\)
  2. Overdamped: \(\zeta > 1\)
  3. Critically Damped:  \(\zeta = 1\)

  • Application

Figure 1 shows a sample application where the response in terms of displacement and velocity as a function of time are to be solved.

Figure 1. Schematic of a SDOF Spring-Mass-Damper Vibration Model.

The given sample of damped system can be represented by the Equation (4).

$$3 \, y''(t) + y'(t) + 2 \, y(t) = 0  \tag{4}$$

With initial conditions as follows

$$y(0) = 0 \quad \text{and} \quad y'(0) = 0.25  \tag{5}$$


  • Numerical Solution Using C++, Codeblocks, Github Copilot, and LabPlot.

Figure 2 is the plot of the numerical solution or the response on the system in terms of displacement and velocity. The code is a modified solution through the assistance of Github Copilot (AI). Codeblocks is used as the Integrated Development Environment (IDE) for the C++. The calculation results from C++ are exported as .csv file. LabPlot is the application used to import the .csv file to make the plot as shown in Figure 2.

Figure 2. Plot of Numerical Solution to Damped System using C++ and LabPlot.

  • C++ Code

#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <iomanip>

// Define the system of ODEs
// y1' = y2
// y2' = f(t, y1, y2) = -(2/3)*y1-(1/3)*y2
double f1(double t, double y1, double y2) {
    return y2; // y1' = y2
}
double f2(double t, double y1, double y2) {
    // Example: y'' = -(2/3)*y1-(1/3)*y2 (Simple Harmonic Oscillator)
    return -(2.0/3.0)*y1-(1.0/3.0)*y2; // y2' = -(2/3)*y1-(1/3)*y2
}
// Runge-Kutta 4th Order Method
void rungeKutta4(double t0, double y10, double y20, double tEnd, double h, const std::string& filename) {
    double t = t0;
    double y1 = y10;
    double y2 = y20;
    // Open the CSV file for writing
    std::ofstream file(filename);
    if (!file.is_open()) {
        std::cerr << "Error: Could not open file " << filename << " for writing.\n";
        return;
    }
    // Write the header to the CSV file
    file << "t,y1,y2\n";
    // Write the initial conditions
    file << t << "," << y1 << "," << y2 << "\n";
    while (t < tEnd) {
        // Compute RK4 coefficients for y1
        double k1_y1 = h * f1(t, y1, y2);
        double k1_y2 = h * f2(t, y1, y2);
        double k2_y1 = h * f1(t + h / 2.0, y1 + k1_y1 / 2.0, y2 + k1_y2 / 2.0);
        double k2_y2 = h * f2(t + h / 2.0, y1 + k1_y1 / 2.0, y2 + k1_y2 / 2.0);
        double k3_y1 = h * f1(t + h / 2.0, y1 + k2_y1 / 2.0, y2 + k2_y2 / 2.0);
        double k3_y2 = h * f2(t + h / 2.0, y1 + k2_y1 / 2.0, y2 + k2_y2 / 2.0);
        double k4_y1 = h * f1(t + h, y1 + k3_y1, y2 + k3_y2);
        double k4_y2 = h * f2(t + h, y1 + k3_y1, y2 + k3_y2);
        // Update y1 and y2
        y1 += (k1_y1 + 2 * k2_y1 + 2 * k3_y1 + k4_y1) / 6.0;
        y2 += (k1_y2 + 2 * k2_y2 + 2 * k3_y2 + k4_y2) / 6.0;
        // Update time
        t += h;
        // Write the results to the CSV file
        file << t << "," << y1 << "," << y2 << "\n";
    }
    // Close the file
    file.close();
    std::cout << "Results saved to " << filename << "\n";
}

int main() {
    // Initial conditions
    double t0 = 0.0;    // Initial time
    double y10 = 0.0;   // Initial value of y (y1)
    double y20 = 0.25;   // Initial value of y' (y2)
    double tEnd = 20.0; // End time
    double h = 0.1;     // Step size
    // Output file name
    std::string filename = "rk4_2nd_ode_output.csv";
    // Solve the ODE and save results to the CSV file
    rungeKutta4(t0, y10, y20, tEnd, h, filename);
    return 0;
}

  • Numerical Solution Using Python, Github Copilot, and VS Code.

Figure 3 is the plot for the response of the same damped system also in terms of displacement and velocity. Github Copilot (Artificial Intelligence) is used to generate the Python script with minor modification. VSCode is used as IDE to run the script and plot the results.

Figure 3. Plot of Numerical Solution to Damped System using Python Script.

  • Python Script

import numpy as np
import matplotlib.pyplot as plt

def runge_kutta_4_system(funcs, y0, t0, t_end, h):
    """
    Solve a system of first-order ODEs using the 4th-order Runge-Kutta method (RK4).
    Parameters:
        funcs (list): List of functions representing the system of ODEs.
        y0 (list): Initial values for the system [y1(0), y2(0), ...].
        t0 (float): Initial time.
        t_end (float): End time.
        h (float): Step size.
    Returns:
        tuple: Arrays of t and y values for each variable.
    """
    t_values = np.arange(t0, t_end + h, h)
    y_values = np.zeros((len(t_values), len(y0)))
    y_values[0] = y0
    for i in range(1, len(t_values)):
        t = t_values[i - 1]
        y = y_values[i - 1]
        k1 = [h * f(t, *y) for f in funcs]
        k2 = [h * f(t + h / 2, *(y + np.array(k1) / 2)) for f in funcs]
        k3 = [h * f(t + h / 2, *(y + np.array(k2) / 2)) for f in funcs]
        k4 = [h * f(t + h, *(y + np.array(k3))) for f in funcs]
        y_values[i] = y + (np.array(k1) + 2 * np.array(k2) + 2 * np.array(k3) + np.array(k4)) / 6
    return t_values, y_values

# Example application
if __name__ == "__main__":
    # Define the system of ODEs for 3y'' + y' + 2y = 0
    # Let y1 = y and y2 = -(2.0/3.0)*y1-(1.0/3.0)*y2, then:
    # y1' = y2
    # y2' = -(2.0/3.0)*y1-(1.0/3.0)*y2
    def f1(t, y1, y2):
        return y2
    def f2(t, y1, y2):
        return -(2.0/3.0)*y1-(1.0/3.0)*y2

    # Initial conditions
    y0 = [0, 0.25]  # y(0) = 0, y'(0) = 0.25
    t0 = 0       # Initial time
    t_end = 20   # End time
    h = 0.1      # Step size
    # Solve the system
    t_values, y_values = runge_kutta_4_system([f1, f2], y0, t0, t_end, h)
    # Extract y1 (the solution to the original ODE)
    y1_values = y_values[:, 0]
    y2_values = y_values[:, 1]

    # Plot the solution
    plt.figure(figsize=(8, 6))
    plt.plot(t_values, y1_values, linewidth=3, label="y(t)", color="blue")
    plt.plot(t_values, y2_values, linestyle='--', linewidth=2, label="y'(t)", color="red")
    plt.title("Using Python to Solve 2nd-Order ODE with RK4")
    plt.xlabel("Time, t")
    plt.ylabel("Displacement, y(t) & Velocity, y'(t)")
    plt.grid(True)
    plt.ylim(-0.3, 0.3)
    plt.legend()
    plt.show()

The damping ratio for the given application can be solved as shown

$$\zeta = \frac{c}{2 \, \sqrt{k \, m}} = \frac{1}{2 \sqrt{(2)(3)}} = 0.204$$

With the damping ratio, \(\zeta = 0.204\), the motion of the response of the system is considered underdamped.

C++ and Python programming languages using Codeblocks and VS Code as IDEs, and LabPlot for plotting application are some powerful engineering tools to analyze a system of mechanical vibration. AI through the use of Github Copilot provided the applicable codes for the solution.

Feel free to comment for inquiry, clarification, correction or suggestion for improvement. Drop your email to make a request to the author.

References

  1. D.I.Inman. Engineering Vibration. 2nd Ed. Prentice Hall International, Inc., New Jersey 07458. 2001.
  2. GitHub Copilot. AI Tools. Accessed on May 8, 2025 within the VS Code environment. 

A Shooting Method Approach to Boundary-Value ODEs

Topic: Differential Equations Boundary-Value Problems Subject: Numerical Methods Tool: Scilab By: Gani Comia, July 2025 The Shooting Me...