function errs = ...
 convergeODERK(use_time_dependent, use_same_timestep, test_odeCFLn, plot_solns)
% convergeODERK: test convergence of ToolboxLS RK integrators on an ODE.
%
% errs = convergeODERK(use_time_dependent, use_same_timestep, ...
%                      test_odeCFLn, plot_solns)
%
% Essentially a script file (only a function to allow subfunctions) to
% investigate the convergence rates of fixed timestep schemes on a simple
% time-dependent or state-dependent ODE.  Used to test whether ToolboxLS
% explicit integrators live up to their purported order of accuracy, at
% least on ODEs (where there is no interference from spatial approximation
% accuracy in the convergence analysis).
%
% Modified from original version to use the schemeFunc calling conventions
% from the Toolbox.  The ODE function (which becomes schemeFunc) also
% returns a CFL timestep bound, which is chosen as a constant.
%
% Input Parameters:
%
%   use_time_dependent: Boolean.  Should we use a time-dependent or
%   state-dependent ODE?  Optional.  Default = 1.
%
%   use_same_timestep: Boolean.  Should we use the maximum CFL bounded
%   timestep, or all the same timestep?  The former will show that multistep
%   schemes are faster.  The latter will show whether there is a difference
%   in the error at a fixed timestep size.  Optional.  Default = 1.
%
%   test_odeCFLn: Boolean.  Test original odeCFLn code, or the new odeCFLsp
%   code?  Note that both options include the standard Shu & Osher order 1,
%   2, 3 schemes, but they use different code to implement them.
%   Optional.  Default = 0.
%
%   plot_solns: Boolean.  Plot each solution as it is generated?
%   Optional.  Default = 0.
%
% Output Parameters:
%
%   errs: An array of the error in the approximate solution at the final
%   time.  Rows are for particular step sizes, columns are for particular
%   integration schemes.

% Copyright 2007 Ian M. Mitchell (mitchell@cs.ubc.ca).  This software is
% used, copied and distributed under the licensing agreement contained in
% the file LICENSE in the top directory of the distribution.
%
% Ian Mitchell, 2/23/07.
% Adapted from code written by Ian Mitchell for CPSC 303, 3/28/06.

if(nargin < 1)
  use_time_dependent = 1;
end

if(nargin < 2)
  use_same_timestep = 1;
end

if(nargin < 3)
  test_odeCFLn = 0;
end

if(nargin < 4)
  plot_solns = 0;
end

%----------------------------------------------------------------------
% Make sure we can see the ToolboxLS kernel m-files.
run('../../addPathToKernel');

%----------------------------------------------------------------------
% What schemes?
if test_odeCFLn
  sources = { 'odecfln', 'odecfln', 'odecfln' };
  ss = { 1, 2, 3 };
  ps = { 1, 2, 3 };
  cfls = { 1.0, 1.0, 1.0 };
  cfl_factor = 1.0;
else
  sources = { 'shu_osher_88', 'shu_osher_88', 'shu_osher_88', ...
              'spiteri_ruuth_02', 'spiteri_ruuth_02', ...
              'spiteri_ruuth_02', 'spiteri_ruuth_02', 'spiteri_ruuth_02', ...
              'spiteri_wang_07' };
  ss = { 1, 2, 3, 3, 4, 4, 5, 5, 5 };
  ps = { 1, 2, 3, 2, 2, 3, 3, 4, 3 };

  % We don't have to worry about actual CFL instability, but we will use
  % the standard CFL numbers to choose our step size.
  if use_same_timestep
    cfls = num2cell(ones(length(sources), 1));
  else
    cfls = { 1.0, 1.0, 1.0, 2.0, 3.0, 2.0, 2.65062, 1.50818, 2.5 };
  end
  cfl_factor = 1.0;
end

% We can use the same plotting style parameters for both types of test.
colors = { 'b', 'b', 'b', 'r', 'r', 'r', 'r', 'r', 'k' };
styles = { ':', '--', '-', '--', '--', '-', '-', '-.', '-' };
symbols = { '+', '+', '+', 'o', 'x', 'o', 'x', 'o', 'x' };

%----------------------------------------------------------------------
num_schemes =  length(sources);

scheme_names = cell(size(sources));
for scheme = 1 : num_schemes,
  scheme_names{scheme} = sprintf('%s, s = %d, p = %d', ...
                                 sources{scheme}, ss{scheme}, ps{scheme});
end

%----------------------------------------------------------------------
% Base timestep (this should ideally be exactly represented in floating point).
h_base = 0.5;
h_const = 1.0;

% Timestep powers (integers).
h_powers = 3:8;
%h_powers = -2:15;
num_powers = length(h_powers);

%----------------------------------------------------------------------
% ODE and analytic solution.
if use_time_dependent
  scheme_func = @time_dependent;
  y0 = 1;
  soln_func = inline('0.5 * exp(-2 * t) + 0.5', 't');
else
  scheme_func = @state_dependent;
  y0 = 1;
  soln_func = inline('exp(0.5 * t)', 't');
end

% IVP parameters.
t_span = [ 0 2 ];

% For plotting solutions.
t_soln = linspace(t_span(1), t_span(2), 1001);
y_soln = feval(soln_func, t_soln);

%----------------------------------------------------------------------
% How many grid points?
Ns = (t_span(2) - t_span(1)) ./ (h_base .^ h_powers);

% For recording the errors for each discretization and scheme.
errs = zeros(num_powers, num_schemes);

%----------------------------------------------------------------------
for scheme = 1 : num_schemes,
  for resolution = 1 : num_powers;

    %------------------------------------------------------------------
    % Step size for this run.
    scheme_data.h = h_const * h_base ^ h_powers(resolution);

    %------------------------------------------------------------------
    % Integrator parameters.
    if strcmp(lower(sources{scheme}), 'odecfln')
      use_odeCFLn = 1;
      if((ss{scheme} < 1) || (ss{scheme} > 3))
        error('odeCFLn option only works with s \in { 1, 2, 3 }');
      end
      integrator_func = str2func([ 'odeCFL' num2str(ss{scheme}) ]);

    else
      use_odeCFLn = 0;
  
      % Pass everything along to odeCFLsp for parsing.
      integrator_func = @odeCFLsp;

      integrator_data.source = sources{scheme};
      integrator_data.s = ss{scheme};
      integrator_data.p = ps{scheme};
    end

    % Some integration parameters apply to any integrator.
    integrator_options = odeCFLset('factorCFL', cfl_factor * cfls{scheme}, ...
                                   'stats', 'on');

    %------------------------------------------------------------------
    if use_odeCFLn
      % Need to distinguish the two cases, since odeCFLsp has an additional
      % parameter.
      [ t_approx y_approx ] = feval(integrator_func, scheme_func, t_span, ...
                                    y0, integrator_options, scheme_data);
    else    
      [ t_approx y_approx ] = feval(integrator_func, scheme_func, t_span, ...
                                    y0, integrator_options, scheme_data, ...
                                    integrator_data);
    end
  
    y_true = feval(soln_func, t_approx);

    errs(resolution, scheme) = abs(y_approx(end) - y_soln(end));
    %errs(resolution, scheme) = (sum(abs(y_approx - y_true)) ...
    %                                  / length(t_approx);

    %------------------------------------------------------------------
    % Plot solution (if necessary).
    if(plot_solns)
      hold off;
      plot(t_soln, y_soln, 'k-');
      hold on;
      plot(t_approx, y_approx, 'b-+');
      title([ func2str(schemes{i}) ', h = ' num2str(h) ]);
      pause;
    end

  end
end

%----------------------------------------------------------------------
% Plot empirical convergence curve, and some lines of fixed slope.
figure;
h = loglog(Ns, errs);
hold on;

for scheme = 1 : num_schemes,
  p = ps{scheme};
  loglog([ min(Ns), min(Ns) * 100 ], ...
         [ errs(1,scheme), errs(1,scheme) * 0.01^p ], 'k-');
end

%----------------------------------------------------------------------
% Escape all occurances of the '_' character so that the TeX
% interpreter does not screw them up.
escaped_scheme_names = strrep(scheme_names, '_', '\_');

title_str = 'Empirical Convergence Rates';
if use_time_dependent
  title_str = strcat(title_str, ' (time-dependent ODE)');
else
  title_str = strcat(title_str, ' (state-dependent ODE)');
end

% Make the plot pretty.
axis tight;
xlabel('Number of grid points');
ylabel('Error');
title(title_str);
[ legh, objh ] = legend(escaped_scheme_names);
for scheme = 1 : num_schemes
  set(h(scheme), 'LineWidth', 2, 'LineStyle', styles{scheme}, ...
                 'Marker', symbols{scheme}, 'Color', colors{scheme});
end


%---------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%---------------------------------------------------------------------------
function [ y_dot, step_bound, scheme_data ] = time_dependent(t, y, scheme_data)
% A smooth time-dependent ODE to integrate.

y_dot = -exp(-2 * t);
step_bound = scheme_data.h;


%---------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%---------------------------------------------------------------------------
function [ y_dot, step_bound, scheme_data] = state_dependent(t, y, scheme_data)
% A smooth state-dependent ODE to integrate.

y_dot = 0.5 * y;
step_bound = scheme_data.h;
