Search code examples
matlabperformancenumericnumerical-methodsnumerical-computing

MATLAB Class Efficiency


I try making it short. I got a problem involving numerical simulation of a flow withing a gas pipe. I simulate using different models for the air flow (transport equations) travelling from one boundary to the other (i.e. from right to left). The question is, since I am using different models to represent said gas flow and boundary conditions I got a lot of functions like:

boundary_left_model_1
boundary_right_model_1
boundary_left_model_2
boundary_right_model_2
boundary_left_model_2b
boundary_right_model_2b
....
flux_model_1
flux_model_2
....
source_model_1
source_model_2
.....

I allocate the needed function before the numerical scheme via (e.g.):

boundary_left = @[needed function];
boundary_right=@ [needed function];
flux = @[needed function];
source=@ [needed function];

What follows is something like:

Solution = numerical_scheme_#1(boundary_left,boundary_right,flux,source,parameters);

you get the point.

Now, what is the most efficient way (while being structured) to build the program? As far as I can tell I have three options:

1) I define every function in a matlab function file (will result in a lot of files)

2) I define every function in the script for the numerical scheme (will result in long file, which is less clear to adjust/read)

3) I create a class for boundary conditions with methods containing all the models, one class for fluxes and sources containing corresponding functions. In my function allocation for the numerical scheme I then call the methods I need. (seems complicated and inefficient, not sure about the time needed calling methods in matlab, but is for me the most structured way)

I should probably note that I am relatively new to matlab and numerical simulation (student) and I am also asking on how this kind of problem is generally solved. Thanks in advance! Also as a bonus: if someone is fond of practical simulation of PDE systems with matlab - I got questions like "How do I decide on a numerical scheme - which criteria should I consider?"

Thanks again, I wish all of you a pleasent day!


Solution

  • Assuming that the methods associated to model 1 are always used together, and not mixed with those for model 2 or 3, then you could set up your code with all functions for one model in one file:

    % MODEL1   Methods that implement model 1
    function [boundary_left,boundary_right,flux,source] = model1
    boundary_left = @boundary_left_method;
    boundary_right = @boundary_right_method;
    flux = @flux_method;
    source = @source_method;
    
    function [out, args] = boundary_left_method(input, args)
      % [implementation]
    end
    
    function [out, args] = boundary_right_method(input, args)
      % [implementation]
    end
    
    function [out, args] = flux_method(input, args)
      % [implementation]
    end
    
    function [out, args] = source_method(input, args)
      % [implementation]
    end
    
    end
    

    Basically, here you have a function that returns the handles to a set of functions that implement one method. boundary_left_method is a private function, so cannot be accessed directly, but model1 can return handles to these functions, which makes them accessible.

    You can now do:

    [boundary_left,boundary_right,flux,source] = model1;
    Solution = numerical_scheme_1(boundary_left,boundary_right,flux,source,parameters);
    

    This solution is fairly similar to the custom class solution suggested by OP, but somewhat simpler. I don't think there would be a big difference in performance, but the only way to know for sure is implementing and timing the various options. What is most efficient changes over time as MATLAB's JIT improves.

    Note that the scheme proposed here also allows for data (parameters) to be included in these functions:

    % MODEL1   Methods that implement model 1
    function [boundary_left,boundary_right,flux,source] = model1(parameters)
    boundary_left = @boundary_left_method;
    boundary_right = @boundary_right_method;
    flux = @flux_method;
    source = @source_method;
    
    function [out, args] = boundary_left_method(input, args)
      out = input * parameters; % You can use PARAMETERS here, note that it is not
                                % an input argument to this nested function, it is
                                % found in the parent scope.
    end
    
    % ...
    
    end % This end here is important now, this way `boundary_left_method` is an
        % nested function, not simply a separate function within the same file.
    

    Now the function handle boundary_left returned has the data of parameters embedded in it. See the relevant documentation.


    If you control also the code to the numerical scheme functions that take these function handles, you could write method1 et al. to return a cell array of handles instead, and the numerical_scheme_1 et al. functions to take a cell array of handles. Then you can simply do:

    numerical_scheme_1(method1,parameters);
    numerical_scheme_1(method2,parameters);
    numerical_scheme_1(method3,parameters);
    numerical_scheme_2(method1,parameters);
    numerical_scheme_2(method2,parameters);
    numerical_scheme_2(method3,parameters);
    % etc.
    

    You could then use a loop to go through all combinations:

    schemes = {@numerical_scheme_1, @numerical_scheme_2, ... };
    methods = {method1, method2, method3, ... };
    for ii=1:numel(schemes)
       for jj=1:numel(methods)
          schemes{ii}(methods{jj},parameters);
       end
    end
    

    [Disclaimer: this code not tested, but I don't see why it wouldn't work...]