This guide provides instructions for submitting and formatting new code in OTP.
Changes to OTP should be proposed as a pull request and undergo a review process before being merged. New code must be free of warnings and errors and adhere to the style guidelines.
Each problem defines a package under +otp that contains all files used by the problem. When creating a new problem we recommend duplicating an existing problem package, e.g., Lorenz63 or Brusselator, then renaming and editing the contents as needed.
To add a new test problem from scratch follow these steps:
- Check out the latest version of OTP:
git clone https://github.com/ComputationalScienceLaboratory/ODE-Test-Problems.git
cd ODE-Test-Problems/- Create a new folder in the
toolbox/+opt/directory with a name that starts with+to indicate it is a package. Also create a subfolder named+presetsin the new problem folder:
mkdir toolbox/+opt/+example
cd toolbox/+opt/+example
mkdir +presets- The minimal set of files needed inside the problem folder to set up a new example problem are:
- The right-hand side (RHS) function named as
f.m - The problem class to specify properties, override plotting and solver options, and convert parameters into arguments for RHS functions
- The parameters class
- A
Canonical.mpreset inside the+presetssubfolder to set standard initial condition and parameters.
touch f.m ExampleProblem.m ExampleParameters.m +presets/Canonical.mThe RHS function f.m, which is the time-derivative of the state y, is defined as a function with at least two arguments. If parameters are needed, they can be added as arguments.
function dy = f(t, y, param1, ...)
dy = ...
endOther functions associated with an otp.RHS like the Jacobian and mass matrix should be implemented in separate .m files with the same function signature as f.m.
A problem package must contain a class named <Name>Problem.m that is a subclass of otp.Problem. There are two methods that must be implemented: the constructor and onSettingsChanged. Optionally, one can override functions such as internalPlot and internalSolve to provide problem-specific defaults. Partitioned problems can add custom RHS functions as class properties with private write access. The property name should start with RHS, e.g., RHSStiff.
A basic template for a new class of problems called Example looks like
classdef ExampleProblem < otp.Problem
methods
function obj = ExampleProblem(timeSpan, y0, parameters)
obj@otp.Problem('Example', [], timeSpan, y0, parameters);
% The second argument specifies the number of variables in the problem is arbitrary
end
end
methods (Access=protected)
function onSettingsChanged(obj)
% Parameters are stored in the obj.Parameters structure. We can assign them to individual variables to be
% used in function calls
param1 = obj.Parameters.Param1;
% set up the RHS function wrapper
obj.RHS = otp.RHS(@(t, y) otp.example.f(t, y, param1));
end
% set up internal plot function
function fig = internalPlot(obj, t, y, varargin)
fig = internalPlot@otp.Problem(obj, t, y, 'xscale', 'log', 'yscale', 'log', varargin{:});
end
% set up internal movie function
function mov = internalMovie(obj, t, y, varargin)
mov = internalMovie@otp.Problem(obj, t, y,, 'xscale', 'log', 'yscale', 'log', varargin{:});
end
% set up internal solver
function sol = internalSolve(obj, varargin)
% Set tolerances due to the very small scales
sol = internalSolve@otp.Problem(obj, 'AbsTol', 1e-50, varargin{:});
end
end
endA problem package must also contain a class named <Name>Parameters.m that is a subclass of otp.Parameters. It needs to provide public properties for each of the problem parameters and a constructor which forwards arguments to the superclass constructor; no methods are needed. Note that property validation is currently not supported in Octave. Therefore, we use a custom comment syntax that is parsed by the installer to optionally include validation. The following is an example of a parameter class with property validation:
classdef ExampleParameters
properties
Param1 %MATLAB ONLY: (1,1) {mustBeReal, mustBeNonnegative}
end
methods
function obj = ExampleParameters(varargin)
obj = obj@otp.Parameters(varargin{:});
end
end
endWithin a problem package, there should be a subpackage named +presets. This contains subclasses of <Name>Problem that specify the time span, initial conditions, and parameters. Typically, only the constructor needs to be implemented in a preset class.
In our example, we add a Canonical.m preset inside the +presets subfolder.
classdef Canonical < otp.example.ExampleProblem
methods
function obj = Canonical(varargin)
y0 = ...
tspan = ...
% Specify a default value for Param1 which can be overridden by a name-value pair passed to this constructor
params = otp.example.ExampleParameters('Param1', pi, varargin{:});
obj = obj@otp.example.ExampleProblem(tspan, y0, params);
end
end
endIn order for this project to maintain a consistent coding style, the following conventions should be used. These standards largely match those most commonly used in MATLAB's code and documentation.
Four spaces are used for indentation. A line should be kept to 120 characters or less.
Variable names should be written in camel case.
% Examples
data = 4;
maxEigenvalue = eigs(rand(4), 1);
fun = @(t, y) y + sin(t);Functions should be completely alphanumeric and written in camel case. No special character is used to distinguish between words.
% Example
function r = depthFirstSearch(tree)
...
endStructures should have camel case property names.
% Example
car = struct('make', 'Ford', 'modelYear', 2020);Package names should be completely lowercase and start with a plus symbol. No capitalization or special character is used to distinguish between words.
% Example
% Path: +otp/+utils/PhysicalConstants.m
help otp.utils.PhysicalConstantsClass names and properties should be written in Pascal case. When the name contains an acronym, all letters should be capitalized. Methods should be written in camel case.
% Examples
classdef Employee
properties
FirstName
LastName
Salary
end
methods
function p = calculatePay(hours)
...
end
end
end
classdef ODETestProblems
...
end