Implementing a new solver

This example demonstrates how to implement a first-order stepper to solve the GHD equation:

The new solver must extend the iFluidSolver class and implement the abstract methods step() and initialize().
The equation above admidts the implicit solution

where the trajectories are given by

and

The equations for the trajectories are also implicit, as the effective velcity and acceleration depends on the state of the system at the given time. The solver presented in this example provides a first-order approximation to the trajectories.

The algorithm

The iFluid solvers must provide a solution for a single time-step

To first order we approximate the trajectories as

and

Defining the class

Create a new solver by extending the iFluidSolver class.

classdef FirstOrderSolver < iFluidSolver

Quantities like the effective and velocity and acceleration are calculated in the iFluidCore class. Therefore, we must pass an object of said class to our solver upon instantiation and store it for future use. This is taken care of by passing the object to the superclass constructor:

function obj = FirstOrderSolver(coreObj, Options)        
    obj = obj@iFluidSolver(coreObj, Options);
end

Implementing the stepper

Next, we implement the abstract method step(), which propagates our system a single time step.

function [theta_next, u_next, w_next] = performFirstOrderStep(obj, theta_prev, u_prev, w_prev, t, dt)            
    [v_eff, a_eff]  = obj.coreObj.calcEffectiveVelocities(theta_prev, t, obj.x_grid, obj.rapid_grid, obj.type_grid); 

    x_tilde         = obj.x_grid - dt*v_eff;
    r_tilde         = obj.rapid_grid - dt*a_eff;

    theta_next      = obj.interpPhaseSpace(theta_prev, r_tilde, x_tilde, obj.extrapFlag);
    u_next          = obj.interpPhaseSpace(u_prev, r_tilde, x_tilde, true ); % always extrapolate u
    w_next          = obj.interpPhaseSpace(w_prev, r_tilde, x_tilde, true ); % always extrapolate u      
end

The method interpPhaseSpace() takes care of interpolating a quantity defined on the pre-specified grids to the points of the trajectories.

Implementing the initializer

Finally, we have the implement the abstract method initialize(), which prepares any quantities needed for the first time step to proceed. The first order algorithm specified here does not require such quantities. Therefore, we don't have to do anything special in the initialize() method.

function [theta, u, w] = initialize(obj, theta_init, u_init, w_init, t_array)
    theta   = theta_init;
    u       = u_init;
    w       = w_init;
end

Application

Once the steps above are completed, the newly implemented solver can be instantiated and used for solving the GHD equation:

Solver1 = FirstOrderSolver( someModel );
theta_t = Solver1.propagateTheta( theta_init, t_array );

Related: How to implement you own model