Problem: 2D Flow Simulation with RK4 and Poisson Solver

The problem requires creating MATLAB functions RK4 for 4th-order Runge-Kutta position update and flow_simulation for 2D particle advection with Poisson pressure solving and RK4 advection.


Input/Output: The problem receives grid size scaling s (integer), aspect ratio ar (scalar), Poisson solver kernel J (3x3 matrix), initial particle positions (from meshgrid 10:15,95:105, reshaped to columns), velocity fields vx, vy (s-by-(s*ar) matrices), and time step h (scalar). It uses the RK4 function (taking current positions px, py, velocity fields vx, vy, h to return new positions) to perform a 2D flow simulation: initializes grids, fields, particles; loops while the figure is active to update velocity, solve Poisson for pressure, advect velocity/particles, replenish particles, and plot particles. Outputs include a figure with particle advection plots, updated velocity/pressure fields, and advected particle positions.

Required dependencies:

Sub-steps:
step_number: 1
step_description_prompt: Please write a MATLAB function named RK4 that implements the 4th-order Runge-Kutta method to update the position (px, py) using velocity fields (vx, vy) with a time step h, returning the new position (x_new, y_new). The function has the definition: [x_new, y_new] = RK4(px, py, vx, vy, h) where px and py are the current x and y positions, vx and vy are 2D velocity field matrices compatible with the interp2 function, and h is the time step. The interp2 function called in the code is assumed to be pre-defined and does not need to be reimplemented. Compute the Runge-Kutta coefficients in the following order: 1. k1x = interp2(vx, px, py); k1y = interp2(vy, px, py); 2. k2x = interp2(vx, px + h/2 * k1x, py + h/2 * k1y); k2y = interp2(vy, px + h/2 * k1x, py + h/2 * k1y); 3. k3x = interp2(vx, px + h/2 * k2x, py + h/2 * k2y); k3y = interp2(vy, px + h/2 * k2x, py + h/2 * k2y); 4. k4x = interp2(vx, px + h * k3x, py + h * k3y); k4y = interp2(vy, px + h * k3x, py + h * k3y); Then calculate the new positions using: x_new = px + h/6 * (k1x + 2*k2x + 2*k3x + k4x); y_new = py + h/6 * (k1y + 2*k2y + 2*k3y + k4y); Return x_new and y_new as the output.
function_header: function [x_new, y_new] = RK4(px, py, vx, vy, h)

end
test_cases: clear; clc;
rng(42); 
[m, n] = deal(6, 6);
vx = 0.1 * ones(m, n);
vy = 0.2 * ones(m, n);
px = 2.5;
py = 3.0;
h = 0.1;
[x_new, y_new] = RK4(px, py, vx, vy, h);
val = x_new + y_new;
correct = 5.530000;
tol = abs(val-correct);
if tol>1e-4
    error('Wrong!')
end

px2 = 1.0;
py2 = 1.0;
[x_new2, y_new2] = RK4(px2, py2, vx, vy, h);
val = x_new2 + y_new2;
correct = 2.030000;
tol = abs(val-correct);
if tol>1e-4
    error('Wrong!')
end

px3 = 3.5;
py3 = 3.5;
[x_new3, y_new3] = RK4(px3, py3, vx, vy, h);
val = x_new3 + y_new3;
correct = 7.030000;
tol = abs(val-correct);
if tol>1e-4
    error('Wrong!')
end
disp('Completely right!')

step_number: 2
step_description_prompt: Please write a MATLAB function named flow_simulation that implements a 2D flow simulation with particle advection, where velocity fields are updated using a Poisson equation solver for pressure and RK4 advection, and particles are advected using RK4.

function flow_simulation()
% Algorithm Flow:
% 1. Clear the command window and workspace, set the random number generator seed to 42, define parameters: grid size scaling `s = 200`, aspect ratio `ar = 2`, and Poisson solver kernel `J = [0 1 0; 1 0 1; 0 1 0] / 4`.
% 2. Create 2D coordinate meshes `X` and `Y` using `meshgrid(1:s*ar, 1:s)`, resulting in `X` and `Y` of size `s-by-(s*ar)`.
% 3. Initialize pressure field `p` and velocity fields `vx`, `vy` as `s-by-(s*ar)` zero matrices using `deal(zeros(s, s*ar))`.
% 4. Define initial particle positions: create a meshgrid `[px, py] = meshgrid(10:15, 95:105)`, reshape both to column vectors with `reshape(px, numel(px), 1)` and `reshape(py, numel(py), 1)`, then store the original positions in `pxo` and `pyo`.
% 5. Create figure 1 and store its handle in `f`.
% 6. Enter a loop that runs while `ishandle(f)` is true:
%    a. Set the `vx` velocity field in the region `95:105` (rows) and `10:15` (columns) to `0.1`.
%    b. Compute the right-hand side (rhs) of the Poisson equation as `-divergence(vx, vy)`.
%    c. Solve the Poisson equation for pressure by iterating 101 times (from `i = 0` to `i = 100`): update `p` using `p = conv2(p, J, 'same') + rhs / 2`.
%    d. Compute the gradient of `p` to get pressure gradient components `dx` and `dy` using `[dx, dy] = gradient(p)`.
%    e. Update the velocity fields by subtracting the pressure gradient (exclude boundary pixels): 
%       `vx(2:end-1, 2:end-1) = vx(2:end-1, 2:end-1) - dx(2:end-1, 2:end-1)`;
%       `vy(2:end-1, 2:end-1) = vy(2:end-1, 2:end-1) - dy(2:end-1, 2:end-1)`.
%    f. Advect the velocity fields using the RK4 method with a time step of `-1`: `[pvx, pvy] = RK4(X, Y, vx, vy, -1)`, then interpolate `vx` and `vy` to the new positions `pvx` and `pvy` using `interp2(vx, pvx, pvy)` and `interp2(vy, pvx, pvy)`.
%    g. Advect the particles using the RK4 method with a time step of `1`: `[px, py] = RK4(px, py, vx, vy, 1)`.
%    h. Replenish particles by appending the original positions `pxo` and `pyo` to `px` and `py`: `px = [px; pxo]`; `py = [py; pyo]`.
%    i. Plot the particles with `scatter(px, py, 1, 'filled')`.
%    j. Set axis properties to equal aspect ratio and limits `[0 s*ar 0 s]` using `axis equal` and `axis([0 s*ar 0 s])`.
%    k. Update the figure immediately with `drawnow`.

% Note: The functions `divergence` and `RK4` are assumed to be pre-defined and available.
ground_truth_code: clc;clear all;
rng(42)
s = 200;
ar = 2;
J = [0 1 0; 1 0 1; 0 1 0]/4;

[X, Y] = meshgrid(1:s*ar, 1:s);

[p, vx, vy] = deal(zeros(s, s*ar));

[px, py] = meshgrid(10:15, 95:105);
px = reshape(px, numel(px), 1);
py = reshape(py, numel(py), 1);

pxo = px;
pyo = py;

f = figure(1);

while ishandle(f)
    vx(95:105, 10:15) = 0.1;

    rhs = -divergence(vx, vy);

    for i = 0:100
        p = conv2(p, J, 'same') + rhs/2;
    end

    [dx, dy] = gradient(p);
    vx(2:end-1, 2:end-1) = vx(2:end-1, 2:end-1) - dx(2:end-1, 2:end-1);
    vy(2:end-1, 2:end-1) = vy(2:end-1, 2:end-1) - dy(2:end-1, 2:end-1);

    [pvx, pvy] = RK4(X, Y, vx, vy, -1);
    vx = interp2(vx, pvx, pvy);
    vy = interp2(vy, pvx, pvy);

    [px, py] = RK4(px, py, vx, vy, 1);

    px = [px; pxo];
    py = [py; pyo];

    scatter(px, py, 1, 'filled');
    axis equal;
    axis([0 s*ar 0 s]);

    drawnow;
end

General solution:

General tests: