%Slurm Interact with a Slurm cluster
%   The Slurm cluster type allows you to submit jobs to a cluster running
%   the Slurm scheduler.
%
%   parallel.cluster.Slurm methods:
%      Slurm                  - Create a Slurm cluster instance
%      batch                  - Run MATLAB script or function as batch job
%      createCommunicatingJob - Create a new communicating job
%      createJob              - Create a new independent job
%      findJob                - Find job objects belonging to a cluster object
%      getDebugLog            - Read output messages from communicating job or independent task
%      getJobClusterData      - Get specific userdata for a job on a Slurm cluster
%      getJobFolder           - Return folder on the client where jobs are stored
%      getJobFolderOnCluster  - Return folder on cluster where jobs are stored
%      getLogLocation         - Get log location for a job or task
%      isequal                - True if clusters have same property values
%      parpool                - Create a parallel pool of workers on a cluster and return a pool object
%      saveAsProfile          - Save modified properties to a profile
%      saveProfile            - Save modified properties to the current profile
%      setJobClusterData      - Set specific userdata for a job on a Slurm cluster
%
%   parallel.cluster.Slurm properties:
%      AdditionalProperties    - Additional properties for plugin scripts
%      ClusterMatlabRoot       - Path to MATLAB on the cluster
%      HasSharedFilesystem     - Specify whether the client and cluster nodes share JobStorageLocation
%      Host                    - Hostname of the cluster's headnode
%      JobStorageLocation      - Folder where cluster stores job and task information
%      Jobs                    - List of jobs contained in this cluster
%      LicenseNumber           - License number to use with online licensing
%      Modified                - True if any properties in this cluster have been modified
%      NumThreads              - Number of computational threads for workers
%      NumWorkers              - Number of workers in the cluster
%      OperatingSystem         - Operating System of the cluster
%      PluginScriptsLocation   - Folder containing scheduler plugin scripts
%      PreferredPoolNumWorkers - Preferred number of workers in a parallel pool
%      Profile                 - Profile used to build this cluster
%      RequiresOnlineLicensing - Cluster uses online licensing
%      SubmitArguments         - Additional options passed to sbatch during job submission
%      Type                    - Type of this cluster
%      UserData                - Data associated with a cluster object in this client session
%
%    See also parcluster, parallel.Cluster.

% Copyright 2017-2023 The MathWorks, Inc.

classdef (ConstructOnLoad = true) Slurm < parallel.cluster.Generic
    properties (Dependent)
        %SubmitArguments - Additional options passed to sbatch during job submission
        %   SubmitArguments is a string that is passed to the Slurm sbatch
        %   command when submitting jobs.
        SubmitArguments
    end

    properties (Hidden, Dependent)
        %UseJobArrays - True if independent jobs with multiple tasks should
        %   be submitted as a Slurm job array
        UseJobArrays

        %CommunicatingJobWrapper - Script that the cluster runs to start workers
        %   When communicating jobs are submitted, this script file is
        %   submitted for execution. The script must contain commands to
        %   launch the communicating job. A script is provided for each of
        %   the 'hydra' (default) and 'smpd' process managers. To modify
        %   how communicating jobs are run, edit the appropriate
        %   communicatingJobWrapper in PluginScriptsLocation.
        CommunicatingJobWrapper

        ResourceTemplate
    end

    methods
        function val = get.SubmitArguments(obj)
            val = obj.additionalPropertyOrDefault('AdditionalSubmitArgs', '');
        end

        function set.SubmitArguments(obj, val)
            obj.AdditionalProperties.AdditionalSubmitArgs = val;
        end

        function val = get.UseJobArrays(obj)
            val = obj.additionalPropertyOrDefault('UseJobArrays', true);
        end

        function set.UseJobArrays(obj, val)
            obj.AdditionalProperties.UseJobArrays = val;
        end

        function val = get.CommunicatingJobWrapper(obj)
            usingSmpd = obj.additionalPropertyOrDefault('UseSmpd', false);
            if usingSmpd
                val = 'smpd';
            else
                val = 'hydra';
            end
        end

        function set.CommunicatingJobWrapper(obj, val)
            arguments
                obj
                val {mustBeTextScalar, mustBeMember(val, ["hydra", "smpd", "default", "mathworks provided script"])}
            end
            if strcmpi(val, 'smpd')
                obj.AdditionalProperties.UseSmpd = true;
            else
                obj.AdditionalProperties.remove('UseSmpd');
            end
        end

        function val = get.ResourceTemplate(~) %#ok<STOUT>
            error(message('parallel:cluster:DeprecatedResourceTemplate'))
        end

        function set.ResourceTemplate(~, ~)
            error(message('parallel:cluster:DeprecatedResourceTemplate'))
        end
    end

    properties (Hidden, SetAccess = immutable)
        ClusterName = ''
        % Can't be sure how to ask the scheduler for its name because we
        % can't guarantee how the plugin scripts are configured.
    end

    methods
        function obj = Slurm(varargin)
            %Slurm Create a Slurm cluster instance
            %   parallel.cluster.Slurm(p1, v1, p2, v2, ...) builds a Slurm
            %   cluster with the specified property values. The properties
            %   can include any of the properties of the Slurm cluster
            %   class, or a profile.
            %
            %   Example:
            %   % Create a Slurm cluster using the 'mySlurm' profile.
            %   myCluster = parallel.cluster.Slurm('Profile', 'mySlurm');
            %
            %   See also parcluster, parallel.Cluster.

            obj@parallel.cluster.Generic(?parallel.cluster.Slurm, varargin{:});

            % By default, use the plugins which ship with PCT
            obj.overrideFactoryValue('PluginScriptsLocation', 'parallel.cluster.slurm');

            % Override OperatingSystem to unix, as Slurm only runs on Linux
            obj.overrideFactoryValue('OperatingSystem', 'unix');
        end
    end

    methods (Access = private)
        function delete(obj)
            delete@handle(obj);
        end
    end

    methods (Hidden)
        function hSetProperty(obj, names, values)
            % Extra checks for properties that this object does not allow to be set.
            if ~iscell(names)
                names  = {names};
                values = {values};
            end
            for ii = 1:length(names)
                name  = names{ii};
                value = values{ii};
                if name == "OperatingSystem" && obj.isNotCurrentValue(name, value)
                    throwAsCaller(obj.buildCannotModify(name));
                end
                obj.hSetProperty@parallel.cluster.Generic(name, value);
            end
        end
    end
end
