Driver class

Contents

Description

This is a handle super-class for the definition of an analysis driver.

An analysis driver is the core of the simulation, being responsible to perform the time steps of the different types of analysis, and also the pre and pos processing tasks.

This super-class defines abstract methods that must be implemented in the derived sub-classes:

classdef Driver < handle

Constant values

    properties (Constant = true, Access = public)
        % Types of analysis
        MECHANICAL        = uint8(1);
        THERMAL           = uint8(2);
        THERMO_MECHANICAL = uint8(3);
    end

Public properties

    properties (SetAccess = public, GetAccess = public)
        % Problem data
        name    string = string.empty;   % simulation name
        type    uint8  = uint8.empty;    % flag for type of analysis
        path_in string = string.empty;   % path to input files folder

        % Model components: handle to objects
        mparts    ModelPart = ModelPart.empty;   % handles to objects of ModelPart class
        particles Particle  = Particle.empty;    % handles to objects of Particle class
        walls     Wall      = Wall.empty;        % handles to objects of Wall class
        interacts Interact  = Interact.empty;    % handles to objects of Interact class
        solids    Material  = Material.empty;    % handles to objects of Material_Solid subclass
        fluid     Material  = Material.empty;    % handles to object of Material_Fluid subclass

        % Model components: total numbers
        n_mparts    uint32 = uint32.empty;   % number of model parts
        n_particles uint32 = uint32.empty;   % number of particles
        n_walls     uint32 = uint32.empty;   % number of walls
        n_interacts uint32 = uint32.empty;   % number of binary interactions
        n_solids    uint32 = uint32.empty;   % number of solid materials

        % Global properties
        gravity    double = double.empty;   % vector of gravity components value
        damp_trl   double = double.empty;   % damping for translational motion
        damp_rot   double = double.empty;   % damping for rotational motion
        fluid_vel  double = double.empty;   % interstitial fluid velocity vector
        fluid_temp double = double.empty;   % interstitial fluid temperature

        % Model domain properties
        alpha      double = double.empty;   % alpha radius
        vol_domain double = double.empty;   % total volume (unity depth) of the alpha-shape polygon of all particles
        porosity   double = double.empty;   % average porosity (void ratio) of the alpha-shape polygon
        por_freq   double = double.empty;   % average porosity update frequency (in steps) (double to accept NaN)
        vor_freq   double = double.empty;   % voronoi diagram update frequency (in steps) (double to accept NaN)
        vor_vtx    double = double.empty;   % vertices coordinates of voronoi diagram
        vor_idx    cell   = cell.empty;     % vertices indices of voronoi diagram cells

        % Total particle properties
        surf_particle  double = double.empty;   % total surface area of all particles
        cross_particle double = double.empty;   % total cross-sectional area of all particles
        vol_particle   double = double.empty;   % total volume of all particles
        mass_particle  double = double.empty;   % total mass of all particles

        % Model limits
        has_bbox logical = logical.empty;   % flag for existing bbox
        has_sink logical = logical.empty;   % flag for existing sinks
        bbox     BBox    = BBox.empty;      % handle to object of BBox class
        sink     Sink    = Sink.empty;      % handles to objects of Sink class

        % Paralelization
        parallel logical = logical.empty;   % flag for parallelization of simulation
        workers  uint16  = uint16.empty;    % number of workers for parallelization

        % Neighbours search
        search Search = Search.empty;   % handle to object of Search class

        % Time integration
        scheme_trl  Scheme = Scheme.empty;   % handle to object of Scheme class for translation integration
        scheme_rot  Scheme = Scheme.empty;   % handle to object of Scheme class for rotation integration
        scheme_temp Scheme = Scheme.empty;   % handle to object of Scheme class for temperature integration

        % Time advancing
        auto_step logical = logical.empty;   % flag for computing time step automatically
        time_step double  = double.empty;    % time step value
        max_time  double  = double.empty;    % maximum simulation time
        time      double  = double.empty;    % current simulation time
        step      double  = double.empty;    % current simulation step (double to perform NaN operations in frequency checks)

        % Elapsed time control (real analysis time)
        start_time double  = double.empty;   % starting elapsed time for current analysis run
        total_time double  = double.empty;   % total elapsed time for all analysis runs

        % Output generation
        path_out   string    = string.empty;      % path to output folder
        save_ws    logical   = logical.empty;     % flag for saving workspace into a storage file
        result     Result    = Result.empty;      % handle to object of Result class
        animations Animation = Animation.empty;   % handles to objects of Animation class
        graphs     Graph     = Graph.empty;       % handles to objects of Graph class
        print      Print     = Print.empty;       % handle to object of Print class

        % Output control
        nprog double  = double.empty;    % progress print frequency (% of total time)
        tprog double  = double.empty;    % next time for printing progress
        nout  double  = double.empty;    % number of outputs
        tout  double  = double.empty;    % next time for storing results
        store logical = logical.empty;   % flag for step to store results
    end

Constructor method

    methods
        function this = Driver(type)
            if (nargin > 0)
                this.type = type;
            end
        end
    end

Abstract methods: implemented in derived sub-classes

    methods (Abstract)
        %------------------------------------------------------------------
        setDefaultProps(this);

        %------------------------------------------------------------------
        setParticleProps(this,particle);

        %------------------------------------------------------------------
        preProcess(this);

        %------------------------------------------------------------------
        process(this);
    end

Public methods

    methods
        %------------------------------------------------------------------
        function initTime(this)
            this.start_time = 0;
            this.time       = 0;
            this.step       = 0;
        end

        %------------------------------------------------------------------
        function initOutputVars(this)
            this.nprog = this.nprog * this.max_time / 100; % convert to time per print
            this.nout  = this.max_time / this.nout;        % convert to time per output
            this.tprog = this.nprog;
            this.tout  = this.nout;
        end

        %------------------------------------------------------------------
        function printProgress(this)
            if (this.time >= this.tprog)
                fprintf('\n%.1f%%: time %.3f, step %d',100*this.time/this.max_time,this.time,this.step);
                this.tprog = this.tprog + this.nprog - 10e-15; % tollerance to deal with precision issues
            end
        end

        %------------------------------------------------------------------
        % Object must be called 'drv' here to load it from storage file.
        function storeResults(drv)
            if (drv.time >= drv.tout)
                if (drv.save_ws)
                    drv.createOutFolder();
                    save(strcat(drv.path_out,drv.name));
                    drv.total_time = drv.start_time + toc;
                end
                if (~isempty(drv.print))
                    drv.print.execute(drv);
                end
                drv.tout = drv.tout + drv.nout - 10e-10; % tollerance to deal with precision issues
                drv.result.updateIndex();
                drv.store = true;
            else
                drv.store = false;
            end
        end

        %------------------------------------------------------------------
        function storeResultsFinal(this)
            if (this.result.idx < length(this.result.times))
                this.result.updateIndex();
                this.result.storeTime(this);
                this.result.storeAvgVelocity(this);
                this.result.storeExtVelocity(this);
                this.result.storeAvgAcceleration(this);
                this.result.storeExtAcceleration(this);
                this.result.storeAvgTemperature(this);
                this.result.storeExtTemperature(this);
                this.result.storeTotalHeatRate(this);
                for i = 1:this.n_particles
                    p = this.particles(i);
                    this.result.storeParticleProp(p);
                    this.result.storeParticlePosition(p);
                    this.result.storeParticleTemperature(p);
                    this.result.storeParticleForce(p);
                    this.result.storeParticleVelocity(p);
                    this.result.storeParticleAcceleration(p);
                    this.result.storeParticleHeatRate(p);
                end
                for i = 1:this.n_walls
                    w = this.walls(i);
                    this.result.storeWallPosition(w);
                    this.result.storeWallTemperature(w);
                end
                if (~isempty(this.print))
                    this.print.execute(this);
                end
            end
        end

        %------------------------------------------------------------------
        function setVoronoiDiagram(this)
            % Assumption: walls are ignored
            try
                [this.vor_vtx,this.vor_idx] = voronoiDiagram(delaunayTriangulation([this.particles.coord]'));
            catch
                fprintf(2,'Could not build Voronoi diagram in step %d\n',this.step);
            end
        end

        %------------------------------------------------------------------
        function setGlobalVol(this)
            % Assumption: in-plane projection area (unity depth)
            vol = area(alphaShape([this.particles.coord]',this.alpha));

            % Avoid null volume (aligned particles)
            % Assumption: set total particle volume (area) when volume is null
            if (vol ~= 0)
                this.vol_domain = vol;
            else
                if (isempty(this.cross_particle))
                    this.cross_particle = sum([this.particles.cross]);
                end
                this.vol_domain = this.cross_particle;
            end
        end

        %------------------------------------------------------------------
        function setGlobalPorosity(this)
            if (isempty(this.vol_domain))
                this.setGlobalVol();
            else
                % Assumption: total volume of particles computed as the
                % in-plane cross-sectional area (unity depth)
                this.porosity = 1 - this.cross_particle/this.vol_domain;
            end
        end

        %------------------------------------------------------------------
        function setTotalParticlesProps(this)
            this.surf_particle  = sum([this.particles.surface]);
            this.cross_particle = sum([this.particles.cross]);
            this.vol_particle   = sum([this.particles.volume]);
            this.mass_particle  = sum([this.particles.mass]);
        end

        %------------------------------------------------------------------
        function do = removeParticle(this,p)
            do = false;

            % Remove particles not respecting bbox
            if (this.has_bbox)
                if (this.bbox.removeParticle(p,this.time))
                    do = true;

                    % Remove particle and its interactions
                    delete(p.interacts);
                    delete(p);

                    % Erase handles from global list of interactions
                    this.interacts(~isvalid(this.interacts)) = [];
                    this.n_interacts = length(this.interacts);
                    return;
                end
            end

            % Remove particles not respecting sinks
            if (this.has_sink)
                for i = 1:length(this.sink)
                    if (this.sink(i).removeParticle(p,this.time))
                        do = true;

                        % Remove particle and its interactions
                        delete(p.interacts);
                        delete(p);

                        % Erase handles from global list of interactions
                        this.interacts(~isvalid(this.interacts)) = [];
                        this.n_interacts = length(this.interacts);
                        return;
                    end
                end
            end
        end

        %------------------------------------------------------------------
        function erasePropsOfRemovedParticle(this)
            % Erase handles from global list
            this.particles(~isvalid(this.particles)) = [];
            this.n_particles = length(this.particles);

            % Erase handles from model parts
            for i = 1:this.n_mparts
                mp = this.mparts(i);
                mp.particles(~isvalid(mp.particles)) = [];
                mp.n_particles = length(mp.particles);
            end

            % Update total particles properties
            this.setTotalParticlesProps();

            % Attention:
            % To avoid additional computational costs, references to the
            % deleted particle from other particles (e.g. neighbohring
            % lists) are not removed.
        end

        %------------------------------------------------------------------
        function createOutFolder(this)
            if (~exist(this.path_out,'dir') ~= 7) % 7 = ID for folders
                warning off MATLAB:MKDIR:DirectoryExists
                mkdir(this.path_out)
                warning on MATLAB:MKDIR:DirectoryExists
            end
        end

        %------------------------------------------------------------------
        function posProcess(this)
            if (isnan(this.result.times(1)))
                fprintf('\nNo valid results to show.\n');
                return;
            end

            % Create and write graphs
            if (~isempty(this.graphs))
                fprintf('\nCreating graphs...\n');
                for i = 1:length(this.graphs)
                    this.graphs(i).execute(this);
                end
            end

            % Create and show animations
            if (~isempty(this.animations))
                for i = 1:length(this.animations)
                    fprintf('\nCreating animation "%s"...',this.animations(i).anim_title);
                    this.animations(i).animate(this);
                end
                fprintf('\n');
                for i = 1:length(this.animations)
                    this.animations(i).showAnimation();
                end
                fprintf('\n');
            end
        end
    end
end