rbmatlab 0.10.01
|
00001 classdef Cached < handle 00002 % Interface for the storage and generation of detailed data that can be used to 00003 % build reduced basis functions. 00004 % 00005 % Via the generate() method, detailed data for exactly one parameter can vector be 00006 % generated and is returned. Usually, this data needs to be postprocessed 00007 % afterwards. E.g. in case of reduced basis generation for evolution equations, 00008 % a Cached instance could produce a detailed solution trajectory for 00009 % the specified parameter and by applying a POD algorithm on this trajectory a 00010 % single basis vector is extracted for basis extension. 00011 % 00012 % The generate method uses caching facilities to prevent data generation twice. 00013 % 00014 % Furthermore, the class provides methods to prepare and store solutions 00015 % before-hand. This can be used in to compare reduced and detailed 00016 % approximations for the entire training or validation set 'M'. 00017 00018 properties(Dependent) 00019 % unique name created from the IDs as given to the constructor. 00020 idname; 00021 end 00022 00023 properties(GetAccess = public, SetAccess = private) 00024 id; % string identifier for this generator 00025 00026 enable_caching = true; % boolean flag indicating whether caching is enabled. 00027 00028 model_name = 'default'; % name id of DetailedModel 00029 end 00030 00031 properties 00032 % boolean value indicating whether the cached data shall be automatically 00033 % deleted if its inconsistent with the given 'dmodel'. 00034 force_delete = true; 00035 00036 % seconds to wait before cached data is deleted automatically in case of 00037 % inconsistent models. 00038 countdown = 5; 00039 end 00040 00041 properties(Dependent) 00042 % checks whether the basis generator points to a valid directory 00043 is_valid; 00044 end 00045 00046 properties(Access = private) 00047 00048 % number of files read from cache (only needed for debugging) 00049 nocachereads = 0; 00050 00051 end 00052 00053 properties(Access = private, Dependent) 00054 % path to all the cached data for the model for which this generator is 00055 % constructed. 00056 modelpath; 00057 00058 % path where the cached functions shall be stored. 00059 filepath; 00060 00061 % full path to settingsfile where model data is stored for consistency 00062 % checks. 00063 settingsfile; 00064 00065 end 00066 00067 00068 methods 00069 00070 function modelpath = get.modelpath(this) 00071 modelpath = fullfile(rbmatlabtemp, 'cache', this.model_name); 00072 end 00073 00074 function filepath = get.filepath(this) 00075 filepath = fullfile(this.modelpath, this.idname); 00076 end 00077 00078 function settingsfile = get.settingsfile(this) 00079 settingsfile = fullfile(this.filepath, 'settings.mat'); 00080 end 00081 00082 function ok = get.is_valid(this) 00083 ok = exist(this.settingsfile, 'file'); 00084 end 00085 00086 function dg = Cached(dmodel, id, force_delete, enable_caching) 00087 % function dg = Cached(dmodel, id, force_delete) 00088 % constructor for the detailed generator 00089 % 00090 % Parameters: 00091 % id: an identifier string or a cell array of identifier 00092 % strings. 00093 % force_delete: boolean value indicating whether the cached data shall 00094 % be automatically deleted if its inconsistent with the 00095 % given 'dmodel'. (Default = 'true') 00096 % enable_caching: boolean value indicating whether the generated data 00097 % shall be stored in a cache. (Default = 'true') 00098 % 00099 dg.id = id; 00100 if nargin >= 3 && ~isempty(force_delete) 00101 dg.force_delete = force_delete; 00102 end 00103 if nargin >= 4 && ~isempty(enable_caching) 00104 dg.enable_caching = enable_caching; 00105 end 00106 dg.model_name = dmodel.name; 00107 if dg.enable_caching 00108 % When NEWDIR exists, MKDIR returns SUCCESS = 1, but with WARNING! 00109 if ~exist(fullfile(rbmatlabtemp, 'cache', dmodel.name, dg.idname), 'dir') 00110 success = mkdir(rbmatlabtemp, fullfile('cache', dmodel.name, dg.idname)); 00111 else 00112 success = true; 00113 end 00114 00115 if ~success 00116 error(['error in generating the caching savepath: ', dg.filepath]); 00117 end 00118 00119 00120 if exist(dg.settingsfile, 'file') 00121 tmp = load(dg.settingsfile); 00122 try 00123 assert_model_is_consistent(dg, dmodel, tmp.dmodel); 00124 catch exception 00125 if ~dmodel.debug && dg.force_delete 00126 disp(exception.message); 00127 disp('=================================================================='); 00128 disp('========================== WARNING: =============================='); 00129 disp('dmodel.force_delete is set to true. The data in the savepath' ); 00130 disp(['will be deleted in ', num2str(dg.countdown), ' seconds. ']); 00131 disp('Press Ctrl-C if you want to keep the precompute files.'); 00132 disp('=================================================================='); 00133 for i=dg.countdown:-1:1 00134 disp(['Deleting in directory in ', num2str(i), ' seconds.']); 00135 pause(1); 00136 end 00137 clear_cachedirectory(dg); 00138 else 00139 error(exception.message); 00140 end 00141 end 00142 end 00143 00144 save(dg.settingsfile, 'dmodel'); 00145 end 00146 end 00147 00148 function idname = get.idname(this) 00149 if iscell(this.id) 00150 idname = sprintf('%s_', this.id{:}); 00151 else 00152 idname = this.id; 00153 end 00154 end 00155 00156 function clear_cachedirectory(this) 00157 % clears the directory containing the cached mat-files 00158 00159 delete(fullfile(this.filepath, '*.mat')); 00160 if exist(this.settingsfile, 'file') 00161 delete(this.settingsfile); 00162 end 00163 end 00164 00165 function assert_model_is_consistent(this, dmodel, comparemodel) 00166 % function assert_model_is_consistent(this, dmodel, comparemodel) 00167 % throws an inconsistency error if the the current IDetailedModel object 00168 % and the one stored on the harddrive differ. 00169 % 00170 00171 iseq = (dmodel == comparemodel); 00172 if ~iseq 00173 throw(MException('RBMatlab:Storage:InconsistentData', ... 00174 ['parameters of precomputed data and current',... 00175 ' simulation are inconsistent! Please delete files in path ', ... 00176 this.filepath, ' and restart!'])); 00177 end 00178 00179 end 00180 00181 function [detailed_sample, tictoc, opt_fields] = generate(this, dmodel, detailed_data, fields) 00182 % function detailed_sample = generate(this, model, detailed_data[, fields]) 00183 % generates a detailed data sample for a specific parameter using 00184 % caching facilities 00185 % 00186 % Internally this method calls the engine method generate_impl() if the 00187 % result cannot be read from the harddrive cache. 00188 % 00189 % Parameters: 00190 % fields: cell array of structure field names which shall also be 00191 % stored in the cache in the 'opt_fields' structure. If not 00192 % given only the 'detailed_sample' is generated. 00193 % 00194 % Return values: 00195 % detailed_sample: This is usually a matrix with DOF vector columns 00196 % holding the generated snapshots. 00197 % tictoc: time needed to compute the snapshots. 00198 % opt_fields: optional fields to be cached. 00199 00200 if nargin <= 3 00201 fields = []; 00202 end 00203 00204 opt_fields = []; 00205 00206 if this.enable_caching 00207 hash = this.hashcode(dmodel); 00208 filename = fullfile(this.filepath, ['dsample', hash, '.mat']); 00209 00210 if exist(filename, 'file') 00211 tmp = load(filename); 00212 cache_hit = true; 00213 if ~isempty(fields) 00214 if ~isfield(tmp, 'opt_fields') 00215 cache_hit = false; 00216 else 00217 for i =1:length(fields) 00218 if ~isfield(tmp.opt_fields, fields{i}) 00219 cache_hit = false; 00220 break; 00221 end 00222 end 00223 opt_fields = tmp.opt_fields; 00224 end 00225 end 00226 detailed_sample = tmp.detailed_sample; 00227 tictoc = tmp.tictoc; 00228 00229 if dmodel.debug 00230 this.nocachereads = this.nocachereads+1; 00231 if mod(this.nocachereads, 10) == 0 00232 disp(['This is the tenth cache read and we are in debug mode.', ... 00233 'Comparing with real simulation...']); 00234 comp = generate_impl(this, dmodel, detailed_data); 00235 if any(size(comp) ~= size(detailed_sample))... 00236 || any(comp ~= detailed_sample) 00237 disp('computations do not correspond with the cached result!!!'); 00238 disp('Please check the hash function of whether the data directory') 00239 disp([' ', this.filepath]); 00240 disp('needs to be deleted'); 00241 keyboard; 00242 end 00243 end 00244 end 00245 00246 if cache_hit 00247 return; 00248 else 00249 disp('cache file not found because opt_fields are missing...'); 00250 end 00251 end 00252 end 00253 00254 tstart = tic; 00255 [detailed_sample, opt_fields] = generate_impl(this, dmodel, detailed_data, fields); 00256 tictoc = toc(tstart); 00257 00258 if this.enable_caching 00259 hash = this.hashcode(dmodel); 00260 filename = fullfile(this.filepath, ['dsample', hash, '.mat']); 00261 00262 save(filename, 'detailed_sample', 'tictoc', 'opt_fields'); 00263 end 00264 00265 end 00266 00267 function prepare(this, dmodel, detailed_data, M) 00268 % function prepare_data(this, dmodel, detailed_data) 00269 % precompute detailed data for all parameters given by \a M. 00270 % 00271 % parameter: 00272 % M: matrix storing the parameters for which the generation shall be 00273 % prepared. The matrix size equal `dim(\cal M) \times M_p`, where `M_p` 00274 % denotes the number of parameter vectors. (Default = []) 00275 00276 num_cpus = 2*dmodel.num_cpus; 00277 00278 tmp_fp = this.filepath; 00279 npar = size(M, 1); 00280 for mout = 0:floor((npar-1)/num_cpus) 00281 dsample_c = cell(1, npar); 00282 tictoc_c = cell(1, npar); 00283 00284 %parfor muind = (mout*num_cpus+1):min(npar, (mout+1)*num_cpus) 00285 for muind = (mout*num_cpus+1):min(npar, (mout+1)*num_cpus) 00286 tmp_M = M; 00287 tmp_dmodel = dmodel; 00288 set_mu( tmp_dmodel, tmp_M(muind,:) ); 00289 tmp_this = this; 00290 00291 hash = tmp_this.hashcode(tmp_dmodel); 00292 filename = fullfile(tmp_fp, ['dsample', hash, '.mat']); 00293 if ~exist(filename, 'file') 00294 disp(['Processing parameter vector ', num2str(muind),'/',num2str(npar),... 00295 ' in Cached ', tmp_this.idname, '...']); 00296 tstart = tic; 00297 dsample_c{muind} = generate_impl(this, tmp_dmodel, detailed_data); 00298 tictoc_c{muind} = toc(tstart); 00299 else 00300 disp(['File exists: Skipping parameter vector ', num2str(muind),'/',num2str(npar),... 00301 ' in Cached ', tmp_this.idname, '...']); 00302 end 00303 end 00304 for muind = (mout*num_cpus+1):min(npar, (mout+1)*num_cpus) 00305 detailed_sample = dsample_c{muind}; 00306 tictoc = tictoc_c{muind}; 00307 00308 set_mu( dmodel, M(muind,:) ); 00309 hash = this.hashcode(dmodel); 00310 filename = fullfile(tmp_fp, ['dsample', hash, '.mat']); 00311 if ~exist(filename, 'file') 00312 save(filename, 'detailed_sample', 'tictoc') 00313 end 00314 end 00315 end 00316 end % end of function prepare_data 00317 00318 function dmodel = get_underlying_model(this) 00319 % function dmodel = get_underlying_model(this) 00320 % returns the detailed model stored on the harddrive with which the 00321 % snaphshots were generated. 00322 if exist(this.settingsfile, 'file') 00323 tmp = load(this.settingsfile); 00324 dmodel = tmp.dmodel; 00325 else 00326 dmodel = ['settingsfile for extension ', this.id, ' does not exist!']; 00327 end 00328 end 00329 00330 00331 end 00332 00333 methods (Abstract, Access=protected) 00334 % generates a sample trajectory or detailed simulation item from which a 00335 % new basis function can be generated. 00336 % 00337 % Parameters: 00338 % fields: cell array of structure field names which shall also be 00339 % stored in the cache in the 'opt_fields' structure. If not 00340 % given only the 'U' is generated. 00341 % 00342 % Return values: 00343 % U: a matrix with DOF vector columns holding the generated 00344 % snapshots. 00345 % opt_fields: optional fields to be cached. 00346 [U, opt_fields] = generate_impl(this, dmodel, detailed_data, fields); 00347 00348 end 00349 00350 methods (Static, Access=protected) 00351 % function hashcode = hashcode(basis_generation) 00352 % this is a very simple hash code, generating an md5 hash out of the 00353 % current parameter vector. 00354 % 00355 % The default implementation of this function simply computes an MD5 hash 00356 % out of the parameter vector; 00357 function hashcode = hashcode(dmodel) 00358 00359 mu = get_mu(dmodel); 00360 hashcode = hash(mu, 'MD5'); 00361 end 00362 00363 end 00364 00365 end