Since this was inspired by the valuable comment of Walter Roberson to my original answer, I am posting this solution as a separate answer.
So it turns out the for-loop solution actually performs 6-8x faster than interp1 inside the ODE RHS function! Here's a plot of both solutions:
Code below.
% simpleStorageTankModel_Matlab Test of Matlab Solver for Simulink problem
clc; clearvars
p1 = [0.03 -0.05 0.01]; % contOutlet = @(t) 0.03*t.^2 - 0.05*t + 0.01;
t1 = 0:0.5:12;
pdi = periodicDiscreteInlet(t1);
discreteInput = [t1', pdi, 0.5*ones(25,1)];
%% Solve ODE in a loop without interp1
fillingLevel1 = zeros(length(t1), 1);
initialFillingLevel = 0.5;
fillingLevel1(1) = initialFillingLevel;
tic
for timeInd = 1:length(t1)-1
odeRhs1 = @(t, y) periodicDiscreteInlet(t1(timeInd)) - polyval(p1, t);
[~, solVec] = ode15s(odeRhs1, linspace(t1(timeInd), t1(timeInd+1), 3), ...
initialFillingLevel);
fillingLevel1(timeInd+1) = solVec(end); % Returns 3 values
initialFillingLevel = solVec(end);
end
toc
%% Solve ODE with interp1
solverOptions = odeset('MaxStep', 1e-2);
modelOdeFun = @(t, y) odeRhs2(t, y, discreteInput, p1);
tic; [~, fillingLevel2] = ode15s(modelOdeFun, t1, 0.5, solverOptions); toc
%% Plot results: V1 as loop, V2 with interpolation
set(groot, 'DefaultLineLineWidth', 1.25);
set(groot, 'DefaultStairLineWidth', 1.25);
figure();
stairs(discreteInput(:,1), discreteInput(:,2), 'b');
hold on; grid on
plot(t1, polyval(p1, t1), 'k');
plot(t1, fillingLevel1, 'r--');
plot(t1, fillingLevel2);
legend({'Inlet', 'Outlet', 'Filling level-loop', 'Filling level-interp1'}, ...
'Location', 'NorthWest');
title('RHS with discrete jumps, loop vs. interpolation');
set(gca, 'FontSize', 12);
%% Separate functions
function dy = odeRhs2(t, y, discreteInput, p1)
% Interpolation as specified in Matlab documentation
% https://mathworks.com/help/matlab/ref/ode45.html#bu3l43b
discreteInput_t = interp1(discreteInput(:,1), discreteInput(:, 2), t, 'previous', 0);
dy = discreteInput_t - polyval(p1, t);
end
function perDisInp = periodicDiscreteInlet(t)
amplitude = 4;
phaseDelay = 0;
pulseWidth = 0.5;
t_cyc = t - floor(t);
if numel(t) > 1
numberOfTimePoints = length(t_cyc);
perDisInp = zeros(numberOfTimePoints, 1);
for timeIndex = 1:numberOfTimePoints
if (t_cyc(timeIndex) >= phaseDelay) && (t_cyc(timeIndex) <...
1 - pulseWidth + phaseDelay)
perDisInp(timeIndex) = amplitude;
end
end
else
if (t_cyc >= phaseDelay) && (t_cyc < 1 - pulseWidth + phaseDelay)
perDisInp = amplitude;
else
perDisInp = 0;
end
end
end