From 7ef80834cb7d2004d9afa5cf967c78bfc686ff38 Mon Sep 17 00:00:00 2001 From: Thorsten Liebig Date: Wed, 16 Nov 2011 13:37:44 +0100 Subject: [PATCH] matlab: Queue interface to run scripts (e.g. sweeps) in parallel --- matlab/Add2Queue.m | 34 ++++++++++++++ matlab/FinishQueue.m | 67 ++++++++++++++++++++++++++++ matlab/InitQueue.m | 81 +++++++++++++++++++++++++++++++++ matlab/ResultsQueue.m | 24 ++++++++++ matlab/RunOpenEMS_Parallel.m | 86 ++++++++++++++++++++++++++++++++++++ matlab/queue_checkProcess.m | 4 +- 6 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 matlab/Add2Queue.m create mode 100644 matlab/FinishQueue.m create mode 100644 matlab/InitQueue.m create mode 100644 matlab/ResultsQueue.m create mode 100644 matlab/RunOpenEMS_Parallel.m diff --git a/matlab/Add2Queue.m b/matlab/Add2Queue.m new file mode 100644 index 0000000..4339b7c --- /dev/null +++ b/matlab/Add2Queue.m @@ -0,0 +1,34 @@ +function [queue] = Add2Queue(queue,func_name, func_args, varargin) +% function [queue] = Add2Queue(queue,func_name, func_args, varargin) +% +% Use this function to add a funtion to the queue. +% +% For more details see: InitQueue +% +% See also: InitQueue, FinishQueue, ResultsQueue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if isfield(queue,'jobs') + jobnum = numel(queue.jobs)+1; +else + jobnum = 1; +end + +queue.jobs{jobnum}.finished = 0; + +queue.jobs{jobnum}.argsfile = [tempname '.mat']; +save(queue.jobs{jobnum}.argsfile,'func_args'); + +queue.jobs{jobnum}.nargout = nargout(func_name); +queue.jobs{jobnum}.outargsfile = [tempname '.mat']; + +queue.jobs{jobnum}.command = [queue.bin queue.bin_options ' "load(''' queue.jobs{jobnum}.argsfile ''');' ... + queue.DependPath ... + '[outargs{1:' num2str(queue.jobs{jobnum}.nargout) '}]=' func_name '(func_args{:});' ... + 'save(''-V7'',''' queue.jobs{jobnum}.outargsfile ''',''outargs'');' ... + 'exit;"']; + +[queue.jobs{jobnum}.pid, queue.jobs{jobnum}.filenames] = queue_addProcess( queue.jobs{jobnum}.command ); diff --git a/matlab/FinishQueue.m b/matlab/FinishQueue.m new file mode 100644 index 0000000..97203b4 --- /dev/null +++ b/matlab/FinishQueue.m @@ -0,0 +1,67 @@ +function [queue] = FinishQueue(queue, query_time) +% function [queue] = FinishQueue(queue, ) +% +% Wait for the given queue to finish. +% +% Parameter: +% query_time (optional): time interval to check for finished tasks +% (in seconds, default is 5) +% +% For more details see: InitQueue +% +% See also: InitQueue, ResultsQueue, Add2Queue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if ~isfield(queue,'jobs') + return +end + +if (nargin<2) + query_time = 5; +end + +numJobs = numel(queue.jobs); + +for n=1:numel(numJobs) + is_done = queue.jobs{n}.finished; +end + +if (queue.verbose>=1) + disp(['FinishQueue: Waiting for ' num2str(sum(~is_done)) ' of ' num2str(numJobs) ' jobs to finish...']); +end + +while sum(is_done)=1) + disp(['FinishQueue: Job #' num2str(n) ' is finished!']); + end + end + end + end +end + +if (queue.verbose>=1) + disp(['FinishQueue: All jobs done!']) +end diff --git a/matlab/InitQueue.m b/matlab/InitQueue.m new file mode 100644 index 0000000..1895ec9 --- /dev/null +++ b/matlab/InitQueue.m @@ -0,0 +1,81 @@ +function [queue] = InitQueue(varargin) +% function [queue] = InitQueue(varargin) +% +% Use this function to initialize a queue to run one or more matlab scripts +% in parallel. +% This can be used to efficiently run an openEMS parameter sweep in parallel +% on multiple remote machines. +% +% Note: +% - Currently only Linux/Unix is supported +% - By default Octave is used to spawn parallel functions (saves +% licenses), but this can be changed by: +% [queue] = InitQueue('UseOctave', 0); +% You may need to change this, if your script is not octave compatible +% - To efficiently run openEMS in parallel, you need to run it on several +% machines using a SSH.host_list setting --> See also RunOpenEMS +% +% Example: +% %serial version: +% for n=1:10 +% % manipulate parameter etc. +% [result1(n) result2(n)] = Parallel_Func_Name(param1, param2); +% end +% +% %parallel version: +% queue = InitQueue('DependPath',{'/opt/openEMS/CSXCAD/matlab', ... +% '/opt/openEMS/openEMS/matlab'}); +% for n=1:10 +% % manipulate parameter etc. +% queue = Add2Queue(queue, 'Parallel_Func_Name', {param1, param2}); +% end +% +% % wait for all to finish +% [queue] = FinishQueue(queue); +% +% % retrieve result +% for n=1:numel(stub_sweep) +% [result1(n) result2(n)] = ResultsQueue(queue,n); +% end +% +% See also: Add2Queue, FinishQueue, ResultsQueue, RunOpenEMS, +% RunOpenEMS_Parallel, FindFreeSSH +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if ~isunix + error 'your OS is not supported (Unix only)' +end + +% use octave as default to save matlab (floating) licenses +% otherwise many matlab instances are spawned +queue.use_octave = 1; + +queue.verbose = 1; + + +% add current path +queue.DependPath = ['addpath(''' pwd ''');']; + +for n=1:2:nargin + if strcmp(varargin{n},'DependPath'); + for m=1:numel(varargin{n+1}) + queue.DependPath = [queue.DependPath 'addpath(''' varargin{n+1}{m} ''');']; + end + end + if strcmp(varargin{n},'UseOctave'); + queue.use_octave = varargin{n+1}; + end +end + + +% set binaries and options +if (queue.use_octave) + queue.bin = ['export LD_LIBRARY_PATH=""; octave']; + queue.bin_options = [' --silent --eval']; +else + queue.bin = [matlabroot '/bin/matlab']; + queue.bin_options = [' -nodesktop -nosplash -r']; +end diff --git a/matlab/ResultsQueue.m b/matlab/ResultsQueue.m new file mode 100644 index 0000000..03c3c28 --- /dev/null +++ b/matlab/ResultsQueue.m @@ -0,0 +1,24 @@ +function [varargout] = ResultsQueue(queue, n) +% function [varargout] = ResultsQueue(queue, n) +% +% Use this function to retrieve the results from a finished queue. +% +% For more details see: InitQueue +% +% See also: InitQueue, FinishQueue, Add2Queue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if n>numel(queue.jobs) + error 'ResultsQueue:job is missing' +end + +if (nargout>numel(queue.jobs{n}.outargs)) + error 'not enough job output arguments' +end + +for n=1:numel(queue.jobs{n}.outargs) + varargout{n} = queue.jobs{n}.outargs{n}; +end diff --git a/matlab/RunOpenEMS_Parallel.m b/matlab/RunOpenEMS_Parallel.m new file mode 100644 index 0000000..408838a --- /dev/null +++ b/matlab/RunOpenEMS_Parallel.m @@ -0,0 +1,86 @@ +function [stdout, stderr] = RunOpenEMS_Parallel(Sim_Paths, Sim_Files, opts, Settings) +% function [stdout, stderr] = RunOpenEMS_Parallel(Sim_Paths, Sim_Files, opts, Settings) +% +% Run multiple openEMS simulations in parallel, distributed on multiple +% machines using a ssh host_list! (currently on Linux only) +% +% This function relies on InitQueue etc. +% +% input: +% Sim_Paths: cell array of pathes to simulate by RunOpenEMS +% Sim_Files: filename or cell array of filenames to simulate +% opts: openEMS options. sa RunOpenEMS +% Settings: use the settings to define multiple host for simulation +% e.g.: Settings.SSH.bin ='/openEMS.sh'; +% Settings.SSH.host_list = {'list','of','hosts'}; +% +% Note: If no SSH host_list is defined, this function will skip the +% parallel run and switch back to a default RunOpenEMS! +% +% See also RunOpenEMS, FindFreeSSH, InitQueue +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig 2011 + +pause_queue = 1; %pause between consecutive runs (needed for FindFreeSSH) + +skip_parallel = 0; + +% currently only supporting linux, run conventional RunOpenEMS +if ~isunix || + warning 'your OS is not supported (Unix only), running default RunOpenEMS'; + skip_parallel = 1; +end + +% in case only one path is given, run conventional RunOpenEMS +if ischar(Sim_Paths) + warning 'only a single path given, running default RunOpenEMS' + skip_parallel = 1; +end + +% in case SSH.host_list is not defined, run conventional RunOpenEMS +if ~isfield(Settings,'SSH') + warning 'SSH options missing, running default RunOpenEMS' + skip_parallel = 1; +elseif ~isfield(Settings.SSH,'host_list') + warning 'SSH.host_list option missing, running default RunOpenEMS' + skip_parallel = 1; +end + +if (skip_parallel) + RunOpenEMS(Sim_Paths, Sim_Files, opts, Settings) + stdout = []; + stderr = []; + return +end + +if ~iscell(Sim_Paths) + error('RunOpenEMS_Parallel:needs a cell array of Sim_Paths to simulate'); +end + +% get the path to this file +[dir] = fileparts( mfilename('fullpath') ); + +queue = InitQueue('DependPath',dir); + +% spawn multiple simulations +numSims = numel(Sim_Paths); +for n=1:numSims + if iscell(Sim_Files) + Sim_File = Sim_Files{n}; + else + Sim_File = Sim_Files; + end + + queue = Add2Queue(queue,'RunOpenEMS',{Sim_Paths{1}, Sim_File, opts, Settings}); + disp(['openEMS simulation #' int2str(n) ' in directory: ' Sim_Paths{n} ' started!']); + pause(pause_queue); +end + +[queue] = FinishQueue(queue); + +for n=1:numel(Sim_Paths) + stdout{n} = queue.jobs{n}.stdout; + stderr{n} = queue.jobs{n}.stderr; +end diff --git a/matlab/queue_checkProcess.m b/matlab/queue_checkProcess.m index 71f4a8c..878165a 100644 --- a/matlab/queue_checkProcess.m +++ b/matlab/queue_checkProcess.m @@ -10,12 +10,12 @@ end if nargout > 1 fid = fopen( filenames.stdout ); - stdout = fread(fid); + stdout = fread(fid, '*char')'; fclose(fid); end if nargout > 2 fid = fopen( filenames.stderr ); - stderr = fread(fid); + stderr = fread(fid, '*char')'; fclose(fid); end