rbmatlab 0.10.01
rbasis/basisgen/+SnapshotsGenerator/Cached.m
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
 All Classes Namespaces Files Functions Variables