diff --git a/_test/common_functions/hashable_obj_tester.m b/_test/common_functions/hashable_obj_tester.m new file mode 100644 index 0000000000..de6e36649d --- /dev/null +++ b/_test/common_functions/hashable_obj_tester.m @@ -0,0 +1,46 @@ +function hashable_obj_tester(hobj,values) +%hashable_obj_tester Generic function which validates correct hashable +%object operations. +% +% Checks if all hashable properties invalidate hash, hash calculations work +% and hash is stored-restored correctly +% +% Inputs: +% hobj -- instance of hashable object, better with proper values set +% Optional +% values-- cellarray of values to set to hashable object. If present, have +% to have number of elements equal to number of hashable +% properties and values of this properties compartible with +% serializeble, i.e. setting property do not contradict to other +% properties, validated through check_combo_arg method +% + +flds = hobj.hashableFields(); +n_flds = numel(flds); +if ~exist('values','var') + values = cellfun(@(x)hobj.(x),flds,'UniformOutput',false); +end + +if ~hobj.hash_defined + hobj = hobj.build_hash(); +end + +for i=1:n_flds + assertTrue(hobj.hash_defined) + hobj.(flds{i}) = values{i}; + assertFalse(hobj.hash_defined) + hobj = hobj.build_hash(); +end + +S = hobj.to_struct(); +rec_obj= hobj.from_struct(S); + +assertTrue(hobj.hash_defined); +assertTrue(hobj == rec_obj) + +obj_arr = [hobj,hobj]; +S = obj_arr.to_struct(); +rec_arr= hobj.from_struct(S); + +assertTrue(rec_arr.hash_defined); +assertTrue(obj_arr == rec_arr) \ No newline at end of file diff --git a/_test/test_instrument_classes/test_IX_fermi_chopper.m b/_test/test_instrument_classes/test_IX_fermi_chopper.m index c4cb2cfa3c..6054f89952 100644 --- a/_test/test_instrument_classes/test_IX_fermi_chopper.m +++ b/_test/test_instrument_classes/test_IX_fermi_chopper.m @@ -41,7 +41,12 @@ obj.save() end - + %-------------------------------------------------------------------------- + function test_hashable_prop(~) + ap = IX_fermi_chopper(12,610,0.049,1.3,0.0228); + hashable_obj_tester(ap); + end + %-------------------------------------------------------------------------- function test_pulse_shape (self) t = -20:0.001:20; diff --git a/_test/test_instrument_classes/test_IX_inst.m b/_test/test_instrument_classes/test_IX_inst.m index ec294808fa..c3f9b59475 100644 --- a/_test/test_instrument_classes/test_IX_inst.m +++ b/_test/test_instrument_classes/test_IX_inst.m @@ -44,7 +44,17 @@ self.save() end - + %-------------------------------------------------------------------------- + function test_hashable_prop_DGfermi(self) + ap = IX_inst_DGfermi (self.mod_DGfermi, self.ap_DGfermi, self.chop_DGfermi); + hashable_obj_tester(ap); + end + function test_hashable_prop_DGdisk(self) + ap = IX_inst_DGdisk (self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk,... + self.hdiv_DGdisk, self.vdiv_DGdisk); + hashable_obj_tester(ap); + end + %-------------------------------------------------------------------------- function test_create_DGfermi (self) % Test creation of Fermi instrument diff --git a/_test/test_instrument_classes/test_IX_mod_shape_mono.m b/_test/test_instrument_classes/test_IX_mod_shape_mono.m index 89e49b795c..e4a4d28d9e 100644 --- a/_test/test_instrument_classes/test_IX_mod_shape_mono.m +++ b/_test/test_instrument_classes/test_IX_mod_shape_mono.m @@ -8,7 +8,7 @@ home_folder; end - + methods %-------------------------------------------------------------------------- function obj = test_IX_mod_shape_mono (name) @@ -18,31 +18,36 @@ end file = fullfile(home_folder,'test_IX_mod_shape_mono_output.mat'); obj@TestCaseWithSave(name,file); - obj.home_folder = home_folder; - - + obj.home_folder = home_folder; + + % Create components needed for an IX_inst_DGdisk % Use an old-ish LET function for convenience obj.efix = 8; instru = let_instrument_struct_for_tests (obj.efix, 280, 140, 20, 2, 2); - + obj.mod_DGdisk = instru.moderator; obj.shape_DGdisk = instru.chop_shape; obj.mono_DGdisk = instru.chop_mono; - + obj.save() end - + + %-------------------------------------------------------------------------- + function test_hashable_prop(self) + ap = IX_mod_shape_mono(self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk); + hashable_obj_tester(ap); + end %-------------------------------------------------------------------------- function test_covariance_mod (self) msm = IX_mod_shape_mono(self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk); msm.shaping_chopper.frequency = 171; msm.energy = self.efix; - + % mod FWHH=99.37us, shape_chop FWHH=66.48us shaped_mod = msm.shaped_mod; % should be false - but only just assertEqualWithSave(self,shaped_mod); - + tcov = msm.covariance(); tmean = msm.mean(); assertEqualToTolWithSave(self, tcov, 'tol', [1e-4,1e-4]) @@ -52,17 +57,17 @@ function test_covariance_mod (self) assertEqualToTol(tcov, tcovR, 'tol', [0.5,2e-2]) assertEqualToTol(tmean, tmeanR, 'tol', [0.5,2e-2]) end - + %-------------------------------------------------------------------------- function test_covariance_shape (self) msm = IX_mod_shape_mono(self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk); msm.shaping_chopper.frequency = 172; msm.energy = self.efix; - + % FWHH=99.37us, shape_chop FWHH=66.09us shaped_mod = msm.shaped_mod; % should be true - but only just assertEqualWithSave(self,shaped_mod); - + tcov = msm.covariance(); tmean = msm.mean(); assertEqualToTolWithSave(self, tcov, 'tol', [1e-4,1e-4]) @@ -72,17 +77,17 @@ function test_covariance_shape (self) assertEqualToTol(tcov, tcovR, 'tol', [0.5,2e-2]) assertEqualToTol(tmean, tmeanR, 'tol', [0.5,2e-2]) end - + %-------------------------------------------------------------------------- function test_covariance_mod_only (self) msm = IX_mod_shape_mono(self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk); msm.shaping_chopper.frequency = 1; msm.energy = self.efix; - + % mod FWHH=99.37us, shape_chop FWHH=11368us shaped_mod = msm.shaped_mod; % should be true - extreme case assertEqualWithSave(self,shaped_mod); - + tcov = msm.covariance(); tmean = msm.mean(); assertEqualToTolWithSave(self, tcov, 'tol', [1e-4,1e-4]) @@ -92,18 +97,18 @@ function test_covariance_mod_only (self) assertEqualToTol(tcov, tcovR, 'tol', [0.5,4e-2]) assertEqualToTol(tmean, tmeanR, 'tol', [0.5,2e-2]) end - + %-------------------------------------------------------------------------- function test_covariance_shaped_only (self) msm = IX_mod_shape_mono(self.mod_DGdisk, self.shape_DGdisk, self.mono_DGdisk); msm.moderator.pp(1)=10000; msm.shaping_chopper.frequency = 171; msm.energy = self.efix; - + % mod FWHH=33947us, shape_chop FWHH=66.48us shaped_mod = msm.shaped_mod; % should be true - extreme case assertTrue(shaped_mod); - + tcov = msm.covariance(); tmean = msm.mean(); assertEqualToTolWithSave(self, tcov, 'tol', [1e-4,1e-4]) @@ -112,7 +117,7 @@ function test_covariance_shaped_only (self) [tcovR,tmeanR] = rand_covariance (msm, npnt); assertEqualToTol(tcov, tcovR, 'tol', [0.5,2e-2]) assertEqualToTol(tmean, tmeanR, 'tol', [0.5,2e-2]) - end + end %-------------------------------------------------------------------------- function test_prev_versions(obj) % Scalar example @@ -133,7 +138,7 @@ function test_prev_versions(obj) check_matfile_IO(verstr, save_variables, sample_files_location ,mod_sm); end end - + end end diff --git a/_test/test_instrument_classes/test_IX_moderator.m b/_test/test_instrument_classes/test_IX_moderator.m index c8633944d9..02503778c8 100644 --- a/_test/test_instrument_classes/test_IX_moderator.m +++ b/_test/test_instrument_classes/test_IX_moderator.m @@ -30,6 +30,11 @@ obj.save() end + %-------------------------------------------------------------------------- + function test_hashable_prop(~) + ap = IX_moderator(15,30,'ikcarp',[5,25,0.13]); + hashable_obj_tester(ap); + end %-------------------------------------------------------------------------- function test_pulse_shape_default (~) diff --git a/_test/test_instrument_classes/test_IX_mosaic.m b/_test/test_instrument_classes/test_IX_mosaic.m index e8d294429f..1fdb177190 100644 --- a/_test/test_instrument_classes/test_IX_mosaic.m +++ b/_test/test_instrument_classes/test_IX_mosaic.m @@ -32,6 +32,12 @@ function test_mosaic_anisotropic(obj) mosaic = IX_mosaic ([1,1,0],[1,-1,0],[10,12,7]); assertEqualWithSave(obj,mosaic); end + + function test_hashable_prop(~) + ap = IX_mosaic ([1,1,0],[1,-1,0],[10,12,7]); + hashable_obj_tester(ap); + end + % function test_mosaic_matrix(obj) xmos = [1,1,0]; diff --git a/_test/test_instrument_classes/test_IX_sample.m b/_test/test_instrument_classes/test_IX_sample.m index 7a3feca7f2..7cc0f679e6 100644 --- a/_test/test_instrument_classes/test_IX_sample.m +++ b/_test/test_instrument_classes/test_IX_sample.m @@ -45,7 +45,11 @@ function test_to_from_struct(~) assertEqual(sample,samp_rec); end - + %-------------------------------------------------------------------------- + function test_hashable_prop(~) + ap = IX_sample([1,0,0], [0,1,0], 'cuboid', [2,3,4], 'eta', 4134); + hashable_obj_tester(ap); + end %-------------------------------------------------------------------------- function test_IX_sample_constructor_error_if_required_args_missing(~) f = @()IX_sample([1,0,0],[0,1,0],'cuboid'); diff --git a/_test/test_instrument_classes/test_IX_source.m b/_test/test_instrument_classes/test_IX_source.m index 342c7eeb6a..9178c2eab7 100644 --- a/_test/test_instrument_classes/test_IX_source.m +++ b/_test/test_instrument_classes/test_IX_source.m @@ -14,6 +14,11 @@ self.save() end + function test_hashable_prop(~) + ap = IX_source('ISIS','-freq',50); + hashable_obj_tester(ap); + end + %-------------------------------------------------------------------------- function test_simple_source(self) ap = IX_source('ISIS','-freq',50); diff --git a/herbert_core/classes/instrument_classes/instrument/@IX_mosaic/IX_mosaic.m b/herbert_core/classes/instrument_classes/instrument/@IX_mosaic/IX_mosaic.m index 513ac546c0..d45081e9bc 100644 --- a/herbert_core/classes/instrument_classes/instrument/@IX_mosaic/IX_mosaic.m +++ b/herbert_core/classes/instrument_classes/instrument/@IX_mosaic/IX_mosaic.m @@ -222,6 +222,7 @@ end function obj=set.parameters(obj,val) obj.parameters_=val; + obj = obj.clear_hash(); end %------------------------------------------------------------------ % Get methods for dependent properties diff --git a/herbert_core/classes/instrument_classes/instrument/detectors/@IX_detector_array/IX_detector_array.m b/herbert_core/classes/instrument_classes/instrument/detectors/@IX_detector_array/IX_detector_array.m index 91b87dd2b1..af7a8b367e 100644 --- a/herbert_core/classes/instrument_classes/instrument/detectors/@IX_detector_array/IX_detector_array.m +++ b/herbert_core/classes/instrument_classes/instrument/detectors/@IX_detector_array/IX_detector_array.m @@ -41,20 +41,20 @@ % my_He3bank = detarray.det_bank(3); % get 3rd detector bank % my_bank.atms = 6.3; % set all gas pressures to 6.3 atms % det_array.det_bank(3) = my_He3bank; % reset the detector bank - - + + properties (Access=private) % Array of IX_detector_bank objects (column vector, length = number % of detector banks) det_bank_ = IX_detector_bank() - + % Path to file source, if any filepath_ = '' - + % Name of file source, if any filename_ = '' end - + properties (Dependent) % Mirrors of private properties; these define object state: % --------------------------------------------------------- @@ -65,7 +65,7 @@ filepath % Name of file source, if any. filename - + % Generic properties across all detector banks: % --------------------------------------------- % Detector identifiers, unique integers greater or equal to one. @@ -95,7 +95,7 @@ % which has components in the secondary spectrometer coordinate frame. % This is an alternative representation of the information in dmat rotvec - + % Other dependent properties: % --------------------------- % Number of detectors summed across all the detector banks (get access @@ -105,7 +105,7 @@ % only) (column vector length equal to number of detector banks) ndet_bank end - + methods %------------------------------------------------------------------ % Constructor @@ -135,7 +135,7 @@ % for a detpar: % 'filename','filepath','group','x2','phi',... % 'azim', 'width', 'height' - + if nargin>0 is_detector_bank = cellfun(@(x)(isa(x,'IX_detector_bank')), varargin); if all(is_detector_bank) @@ -144,7 +144,7 @@ tmp = cellfun (@(x)(x(:)), varargin, 'uniformOutput', false); obj.det_bank_ = cat(1,tmp{:}); clear tmp - + % Check that the detector identifiers are all unique id = arrayfun (@(O)(O.id), obj.det_bank_, 'uniformOutput', false); id_all = cat(1,id{:}); @@ -152,7 +152,7 @@ error ('HERBERT:IX_detector_array:invalid_argument',... 'Detector identifiers must all be unique') end - + elseif numel(varargin)==1 && isstruct(varargin{1}) % Single argument that is a structure. Assume attempt to % initialise with a detpar structure @@ -162,9 +162,9 @@ 'Detpar structure must be a scalar structure') elseif all( isfield(S, {'filename','filepath',... 'group','x2','phi','azim', 'width', 'height'})) - + % get the default types and parameters for a detector - these should have been - % set by the user before use here. + % set by the user before use here. % NOTE - this is an interim solution to obtain % detector parameter values until they are % available in nxspe. #1338 tracks the need to @@ -193,9 +193,9 @@ IX_det_TobyfitClassic (S.width, S.height)); else error('HORACE:IX_detector_array:invalid_type', ... - 'unsupported detector type'); + 'unsupported detector type'); end - + obj.filename_ = S.filename; obj.filepath_ = S.filepath; else @@ -204,7 +204,7 @@ '''filename'',''filepath'',''group'',''x2'','... '''phi'',''azim'', ''width'', ''height''']) end - + else % Delegate processing of varargin to IX_detector_bank. % This implies that varargin is the whole set of @@ -212,13 +212,13 @@ obj.det_bank_ = IX_detector_bank (varargin{:}); end end - + end - + %------------------------------------------------------------------ % Set methods for dependent properties %------------------------------------------------------------------ - + % Mirrors of private properties; these define object state: % --------------------------------------------------------- function obj = set.det_bank (obj, val) @@ -231,7 +231,7 @@ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.filepath (obj, val) if isempty(val) @@ -245,7 +245,7 @@ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.filename (obj, val) if isempty(val) @@ -259,7 +259,7 @@ obj = obj.check_combo_arg(); end end - + % Generic properties across all detector banks: % --------------------------------------------- function obj = set.id (obj, val) @@ -284,12 +284,12 @@ tmp(i).id = val(nbeg(i):nend(i)); end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.x2 (obj, val) ndets = obj.ndet_bank; @@ -313,12 +313,12 @@ end end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.phi (obj, val) ndets = obj.ndet_bank; @@ -342,12 +342,12 @@ end end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.azim (obj, val) ndets = obj.ndet_bank; @@ -371,12 +371,12 @@ end end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.dmat (obj, val) ndets = obj.ndet_bank; @@ -407,12 +407,12 @@ end end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - + %--------------------------- function obj = set.rotvec (obj, val) ndets = obj.ndet_bank; @@ -443,30 +443,30 @@ end end obj.det_bank_ = tmp; - + if obj.do_check_combo_arg_ obj = obj.check_combo_arg(); end end - - + + %------------------------------------------------------------------ % Get methods for dependent properties %------------------------------------------------------------------ - + % Mirrors of private properties; these define object state: function val = get.det_bank (obj) val = obj.det_bank_; end - + function val = get.filename (obj) val = obj.filename_; end - + function val = get.filepath (obj) val = obj.filepath_; end - + % Generic properties across all detector banks: function val = get.id(obj) if numel(obj.det_bank_)>1 @@ -476,7 +476,7 @@ val = obj.det_bank_.id; end end - + function val = get.group(obj) val = obj.id'; end @@ -489,7 +489,7 @@ val = obj.det_bank_.x2; end end - + function val = get.phi(obj) if numel(obj.det_bank_)>1 tmp = arrayfun (@(O)(O.phi), obj.det_bank_, 'uniformOutput', false); @@ -498,7 +498,7 @@ val = obj.det_bank_.phi; end end - + function val = get.azim(obj) if numel(obj.det_bank_)>1 tmp = arrayfun (@(O)(O.azim), obj.det_bank_, 'uniformOutput', false); @@ -507,7 +507,7 @@ val = obj.det_bank_.azim; end end - + function val = get.dmat(obj) if numel(obj.det_bank_)>1 tmp = arrayfun(@(O)(O.dmat), obj.det_bank_, 'uniformOutput', false); @@ -516,7 +516,7 @@ val = obj.det_bank_.dmat; end end - + function val = get.rotvec(obj) if numel(obj.det_bank_)>1 tmp = arrayfun(@(O)(O.rotvec), obj.det_bank_, 'uniformOutput', false); @@ -525,7 +525,7 @@ val = obj.det_bank_.rotvec; end end - + function val = get.ndet(obj) if numel(obj.det_bank_)>1 val = sum(arrayfun (@(O)(O.ndet), obj.det_bank_)); @@ -533,7 +533,7 @@ val = obj.det_bank_.ndet; end end - + function val = get.ndet_bank(obj) if numel(obj.det_bank_)>1 val = arrayfun (@(O)(O.ndet), obj.det_bank_); @@ -542,8 +542,8 @@ end end end - - methods + + methods function val = get_detpar_representation(obj) %GET_DETPAR_REPRESENTATION convert first detector bank into detpar struct % intended for use initialising from a *default* IX_detector_array @@ -553,17 +553,17 @@ val.x2 = obj.det_bank_(1).x2; val.phi = obj.det_bank_(1).phi; val.azim = obj.det_bank_(1).azim; - val.width = obj.det_bank_(1).width; - val.height = obj.det_bank_(1).height; + val.width = obj.det_bank_(1).width; + val.height = obj.det_bank_(1).height; val.filename = obj.filename_; - val.filepath = obj.filepath_; + val.filepath = obj.filepath_; end function obj = check_combo_arg(obj) - % TODO: at least array equal length should be validated - obj = obj.clear_hash(); + % TODO: at least array equal length should be validated + obj = obj.clear_hash(); end end - + methods(Static) function is_dp_struct = check_detpar_parms(dp) % checks input dp to see if it is a proper old-style detpar struct. @@ -584,26 +584,26 @@ is_dp_struct = isstruct(dp) && all( isfield(dp,{'group','x2','phi','azim', ... 'filename','filepath','width','height'})); - end + end end - + %====================================================================== % SERIALIZABLE INTERFACE %====================================================================== - + methods function ver = classVersion(~) % Current version of class definition ver = 1; end - + function flds = saveableFields(~) % Return cellarray of properties defining the class flds = {'det_bank', 'filename', 'filepath'}; end - + end - + %------------------------------------------------------------------ methods (Static) function obj = loadobj(S) @@ -614,5 +614,4 @@ end end %====================================================================== - end diff --git a/herbert_core/utilities/classes/@hashable/hashable.m b/herbert_core/utilities/classes/@hashable/hashable.m index e343f2917d..1f383e4669 100644 --- a/herbert_core/utilities/classes/@hashable/hashable.m +++ b/herbert_core/utilities/classes/@hashable/hashable.m @@ -42,7 +42,8 @@ val = obj.hash_value_; end function is = get.hash_defined(obj) - is = ~isempty(obj.hash_value_); + is = arrayfun(@(x)~isempty(x.hash_value_),obj); + is = all(is); end function S = to_struct (obj) diff --git a/herbert_core/utilities/classes/@hashable/private/to_hashable_array_.m b/herbert_core/utilities/classes/@hashable/private/to_hashable_array_.m index 14e5f05a37..894fb1aade 100644 --- a/herbert_core/utilities/classes/@hashable/private/to_hashable_array_.m +++ b/herbert_core/utilities/classes/@hashable/private/to_hashable_array_.m @@ -37,7 +37,7 @@ if iscell(hash) % use array of hashes as source for final hash hash = strjoin(hash ,''); end - tm = typecast(hash,'uint8'); + tm = uint8(hash); % beware of possibility hash changing type in a future arr{ic} = tm(:); elseif isa(val,'double') tm = typecast(single(round(val(:),7)),'uint8');