Master class
Contents
Description
This is the main class for running simulations and tests in DEMLab.
It is responsible for managing the high-level tasks and call the appropriate methods to perform each stage of a simulation, from the reading of input files to the showing of results.
classdef Master < handle
Public properties
properties (SetAccess = public, GetAccess = public) echo uint8 = uint8.empty; % echo level (amount of information to be printed in command window) path_in string = string.empty; % path to input files folder files string = string.empty; % full name of input files (with path) cur_file string = string.empty; % name of current file being run is_last logical = logical.empty; % flag for last input file to run end
Constructor method
methods function this = Master() this.setDefaultProps(); end end
Public methods: main functions
methods %------------------------------------------------------------------ function setDefaultProps(this) this.echo = 1; end %------------------------------------------------------------------ function runSimulations(this,echo) if (nargin > 1) this.echo = echo; end % Print header this.printHeader(); % Get input files if (~this.getInputFiles()) return; end % Display input files this.displayInputFiles(); % Run each input file for i = 1:length(this.files) this.is_last = (i == length(this.files)); % Clear driver clearvars drv; % Get current file name and extension this.cur_file = this.files(i); [~,~,ext] = fileparts(this.cur_file); % Run according to file extension if (strcmp(ext,'.json')) [status,drv] = this.runAnalysis(); if (~status) continue; end elseif (strcmp(ext,'.mat')) [status,drv] = this.loadResults(); if (~status) continue; end else fprintf('Input file:\n%s\n',this.cur_file); fprintf(2,'\nInvalid input file extension.\n'); this.printExit(); continue; end % Pos-process drv.posProcess(); % Finish current analysis fprintf('\nFinished!\n'); if (~this.is_last) fprintf('\n------------------------------------------------------------------\n\n'); end end end %------------------------------------------------------------------ function runTests(this,mode) this.echo = 0; % Print header this.printHeader(); % Check input if (mode ~= 1 && mode ~= 2) fprintf('Invalid testing mode!\n'); fprintf('\nExiting program...\n'); return; end % Get input files if (~this.getInputFiles()) return; end % Display input files this.displayInputFiles(); % Run each input file for i = 1:length(this.files) this.is_last = (i == length(this.files)); % Clear driver clearvars drv; % Get current file name and extension this.cur_file = this.files(i); [~,~,ext] = fileparts(this.cur_file); if (~strcmp(ext,'.json')) fprintf('Input file:\n%s\n',this.cur_file); fprintf(2,'\nInvalid input file extension.\n'); this.printExit(); continue; end % Run simulation [status,drv] = this.runAnalysis(); if (~status) continue; end % Pos-process drv.posProcess(); % Perform action according to testing mode if (mode == 1) % compare results file with reference this.compareTest(drv); elseif (mode == 2) % generate/update reference results this.renameRefResultFile(drv); end % Finish current analysis if (~this.is_last) fprintf('\n------------------------------------------------------------------\n\n'); end end fprintf('\nFinished!\n'); end %------------------------------------------------------------------ function [status,drv] = runAnalysis(this) % Open parameters file fprintf('Parameters file:\n%s\n',this.cur_file); fid = fopen(this.cur_file,'rt'); if (fid < 0) fprintf(2,'\nError opening parameters file.\n'); this.printExit(); status = 0; return; end % Read parameters file if (this.echo > 0) fprintf('\nReading parameters file...\n'); end read = Read(); [status,drv,storage_file] = read.execute(this.path_in,fid); % Pre analysis tasks if (status == 0) this.printExit(); return; elseif (status == 1) % start analysis from the beggining % Check input data if (this.echo > 0) fprintf('\nChecking consistency of input data...\n'); end if (~read.check(drv)) this.printExit(); status = 0; return; end % Pre-process if (this.echo > 0) fprintf('\nPre-processing...\n'); end if (~drv.preProcess()) this.printExit(); status = 0; return; end % Print simulation information if (this.echo > 0) this.printSimulationInfo(drv); fprintf('\nStarting analysis:\n'); fprintf('%s\n',datestr(now)); end elseif (status == 2) % continue analysis from previous state f = dir(storage_file); fprintf('\nStorage file found (%.3f Mb):\n%s\n',f.bytes/10e5,storage_file); % Load storage file [loaded,drv] = this.loadStorageFile(storage_file); if (~loaded) status = 0; return; end % Set output folder to current folder drv.path_out = this.path_in; % Update starting elapsed time drv.start_time = drv.total_time; % Print simulation information if (this.echo > 0) this.printSimulationInfo(drv); fprintf('\nStarting analysis from previous results:\n'); fprintf('%s\n',datestr(now)); end end % Show starting configuration if (this.echo > 0) Animation().curConfig(drv,'Starting'); end % Execute analysis tic; drv.process(); % Print finished status if (this.echo > 0) this.printFinishedStatus(drv,status); end end %------------------------------------------------------------------ function [status,drv] = loadResults(this) status = 1; f = dir(this.cur_file); fprintf('Storage file (%.3f Mb):\n%s\n',f.bytes/10e5,this.cur_file); % Load storage file [loaded,drv] = this.loadStorageFile(this.cur_file); if (~loaded) status = 0; return; end % Set output folder to current folder drv.path_out = this.path_in; % Show current configuration if (this.echo > 0) Animation().curConfig(drv,''); end end end
Public methods: auxiliary functions
methods %------------------------------------------------------------------ function printHeader(~) fprintf('==================================================================\n'); fprintf(' DEMLab - Discrete Element Method Laboratory \n'); fprintf(' Version 1.0 - May 2022 \n'); fprintf(' International Center for Numerical Methods in Engineering (CIMNE)\n'); fprintf(' Polytechnic University of Catalonia (UPC BarcelonaTech) \n'); fprintf('==================================================================\n\n'); end %------------------------------------------------------------------ function status = getInputFiles(this) status = 1; % Get files from dialog filter = {'*.json','Parameters File (*.json)';'*.mat','Storage File (*.mat)'}; title = 'DEMLab - Input file'; default = 'ProjectParameters.json'; [file_names,path] = uigetfile(filter,title,default,'MultiSelect','on'); if (isequal(file_names,0)) fprintf('No file selected.\n'); fprintf('\nExiting program...\n'); status = 0; return; end file_fullnames = fullfile(path,file_names); % Convert to string array this.files = string(file_fullnames); this.path_in = string(path); end %------------------------------------------------------------------ function displayInputFiles(this) n_files = length(this.files); fprintf('%d input files selected:\n',n_files); for i = 1:n_files fprintf('%s\n',this.files(i)); end fprintf('\n------------------------------------------------------------------\n\n'); end %------------------------------------------------------------------ function printSimulationInfo(~,drv) fprintf('\nSimulation ready:\n'); if (~isempty(drv.name)) fprintf('Name...................: %s\n',drv.name); end switch drv.type case drv.MECHANICAL fprintf('Type...................: Mechanical\n'); case drv.type == drv.THERMAL fprintf('Type...................: Thermal\n'); case drv.THERMO_MECHANICAL fprintf('Type...................: Thermo-mechanical\n'); end if (~isempty(drv.n_walls)) fprintf('Number of walls........: %d\n',drv.n_walls); end if (~isempty(drv.n_particles)) fprintf('Number of particles....: %d\n',drv.n_particles); end if (~isempty(drv.particles)) ravg = mean([drv.particles.radius]); rdev = std([drv.particles.radius]); rmin = min([drv.particles.radius]); rmax = max([drv.particles.radius]); if (rdev/rmin < 1e-8) rdev = 0; end fprintf('Average radius.........: %.3e\n',ravg); fprintf('Radius deviation.......: %.3e\n',rdev); if (rdev ~= 0) fprintf('Min radius.............: %.3e\n',rmin); fprintf('Max radius.............: %.3e\n',rmax); end end if (~isempty(drv.particles) &&... (drv.type == drv.THERMAL || drv.type == drv.THERMO_MECHANICAL)) tavg = mean([drv.particles.temperature]); tdev = std([drv.particles.temperature]); tmin = min([drv.particles.temperature]); tmax = max([drv.particles.temperature]); fprintf('Average temperature....: %.3e\n',tavg); fprintf('Temperature deviation..: %.3e\n',tdev); if (tdev ~= 0) fprintf('Min temperature........: %.3e\n',tmin); fprintf('Max temperature........: %.3e\n',tmax); end end if (~isempty(drv.mass_particle)) fprintf('Total mass.............: %.3e\n',drv.mass_particle); end if (~isempty(drv.time_step)) fprintf('Time step..............: %.3e\n',drv.time_step); end if (~isempty(drv.max_time)) fprintf('Final time.............: %f\n',drv.max_time); end end %------------------------------------------------------------------ function printFinishedStatus(~,drv,status) fprintf('\n\nAnalysis finished:\n'); fprintf('%s\n',datestr(now)); if (status == 1) time = seconds(toc); avg_time = time/drv.step; time.Format = 'hh:mm:ss.SS'; avg_time.Format = 's'; fprintf('Total time:.....: %s\n',string(time)); fprintf('Avg step time:..: %s\n',string(avg_time)); elseif (status == 2) curr_time = seconds(toc); total_time = seconds(drv.total_time); avg_time = total_time/drv.step; curr_time.Format = 'hh:mm:ss.SS'; total_time.Format = 'hh:mm:ss.SS'; avg_time.Format = 's'; fprintf('Current analysis time:..: %s\n',string(curr_time)); fprintf('Total simulation time:..: %s\n',string(total_time)); fprintf('Avg step time:..........: %s\n',string(avg_time)); end end %------------------------------------------------------------------ function printExit(this) if (this.is_last) fprintf('\nExiting program...\n'); else fprintf('\nAborted!\n'); fprintf('\n------------------------------------------------------------------\n\n'); end end %------------------------------------------------------------------ function [status,drv] = loadStorageFile(this,storage_file) status = 1; try warning off MATLAB:load:variableNotFound load(storage_file,'drv'); warning on MATLAB:load:variableNotFound catch fprintf(2,'\nError loading storage file.\n'); this.printExit(); status = 0; return; end if (~exist('drv','var') || isempty(drv)) fprintf(2,'\nInvalid stored data.\n'); this.printExit(); status = 0; return; end end %------------------------------------------------------------------ function compareTest(this,drv) % Check if reference and current results files exist name_ref = strcat(drv.path_in,drv.name,"_ref.pos"); name_cur = strcat(drv.path_out,drv.name,".pos"); if (exist(name_ref,'file') ~= 2) fprintf(2,'\nMissing reference results file!\n'); this.deleteFile(name_cur); status = rmdir(drv.path_out); %#ok<NASGU> return; end if (exist(name_cur,'file') ~= 2) fprintf(2,'\nCurrent results file was not generated correctly!\n'); this.deleteFile(name_cur); status = rmdir(drv.path_out); %#ok<NASGU> return; end % Compare contents of files file_ref = javaObject('java.io.File',name_ref); file_cur = javaObject('java.io.File',name_cur); is_equal = javaMethod('contentEquals','org.apache.commons.io.FileUtils',file_ref,file_cur); if (is_equal) fprintf(1,'\nTest passed!\n'); else fprintf(2,'\nResults are different!\n'); end % Delete current results file and folder (if empty) this.deleteFile(name_cur); status = rmdir(drv.path_out); %#ok<NASGU> end %------------------------------------------------------------------ function renameRefResultFile(this,drv) % Check if current results file exist name_cur = strcat(drv.path_out,drv.name,".pos"); if (exist(name_cur,'file') ~= 2) fprintf(2,'\nCurrent results file was not generated correctly!\n'); this.deleteFile(name_cur); status = rmdir(drv.path_out); %#ok<NASGU> return; end % Move current results file out of output folder if (~movefile(name_cur,drv.path_in)) fprintf(2,'\nCurrent results file was not generated correctly!\n'); this.deleteFile(name_cur); status = rmdir(drv.path_out); %#ok<NASGU> return; end % Delete output folder (if empty) status = rmdir(drv.path_out); %#ok<NASGU> % Rename current results file to reference results file name_cur = strcat(drv.path_in,drv.name,".pos"); name_ref = strcat(drv.path_in,drv.name,"_ref.pos"); movefile(name_cur,name_ref); fprintf(1,'\nReference results generated!\n'); end %------------------------------------------------------------------ function deleteFile(~,file_name) if (exist(file_name,'file') == 2) warning off MATLAB:DELETE:FileNotFound delete(sprintf('%s',file_name)); warning on MATLAB:DELETE:FileNotFound end end %------------------------------------------------------------------ % To be called before the analysis (currently not available) function startParallel(~,drv) p = gcp('nocreate'); if (drv.parallel) max_workers = parcluster('local').NumWorkers; if (drv.workers > max_workers) drv.workers = max_workers; warning('off','backtrace'); warning('The selected number of workers is greater than the available number of workers in this machine. The simulation will proceed with the %d available workers',max_workers); warning('on','backtrace'); end if (isempty(p)) fprintf('\n'); parpool(drv.workers); elseif (p.NumWorkers ~= drv.workers) fprintf('\n'); delete(p) parpool(drv.workers); end elseif (~isempty(p)) fprintf('\n'); delete(p); end ps = parallel.Settings; ps.Pool.AutoCreate = false; end end
end