diff --git a/_test/test_experiment/test_experiment_constructor.m b/_test/test_experiment/test_experiment_constructor.m index 7ebddf0f8c..59778ac14d 100644 --- a/_test/test_experiment/test_experiment_constructor.m +++ b/_test/test_experiment/test_experiment_constructor.m @@ -95,7 +95,7 @@ function test_load_save_default_object_creates_default_object(~) load(tmpfile, 'expt'); assertTrue( isempty(expt.instruments)); assertTrue( isempty(expt.samples)); - assertEqual(expt.detector_arrays, []); + assertEqual(expt.detector_arrays, IX_detector_array.empty); end function test_instruments_setter_updates_value_for_valid_value(~) diff --git a/_test/test_sqw_class/test_equal_to_tol.m b/_test/test_sqw_class/test_equal_to_tol.m index 8eac96b27a..d775a2df9d 100644 --- a/_test/test_sqw_class/test_equal_to_tol.m +++ b/_test/test_sqw_class/test_equal_to_tol.m @@ -65,7 +65,7 @@ function test_different_sqw_objects_are_not_equal(obj) sqw_copy = obj.sqw_2d; field_name = class_fields{idx}; - if isstruct(sqw_copy.(field_name)) + if isstruct(sqw_copy.(field_name)) && ~isempty(sqw_copy.(field_name)) sqw_copy.(field_name).test_field = 'test_value'; elseif isstring(sqw_copy.(field_name)) sqw_copy.(field_name) = 'test_value'; diff --git a/_test/test_sqw_class/test_sqw_constructor.m b/_test/test_sqw_class/test_sqw_constructor.m index bda7dba358..37091c1599 100644 --- a/_test/test_sqw_class/test_sqw_constructor.m +++ b/_test/test_sqw_class/test_sqw_constructor.m @@ -49,7 +49,7 @@ function test_filename_constructor_returns_populated_class(obj) assertEqual(numel(sqw_obj.experiment_info.expdata), 85) assertEqual(numel(sqw_obj.experiment_info.instruments), 85) assertEqual(numel(sqw_obj.experiment_info.samples), 85) - assertEqual(numel(sqw_obj.detpar.group), 36864); + assertEqual(numel(sqw_obj.experiment_info.detector_arrays.id), 36864); assertEqual(numel(sqw_obj.data.pax), 1); assertEqual(sqw_obj.data.pix.num_pixels, 100337); end @@ -86,19 +86,32 @@ function test_copy_constructor_returns_distinct_object(obj) sqw_obj = sqw(obj.test_sqw_1d_fullpath); sqw_copy = sqw(sqw_obj); + % combine value changes and tests for them + % (1) change main_header sqw_copy.main_header.title = 'test_copy'; + assertFalse(equal_to_tol(sqw_copy.main_header, sqw_obj.main_header)); + + % (2) change detpar values now in experiment_info + expinf1 = sqw_copy.experiment_info; + for i=1:numel(expinf1.detector_arrays), expinf1.detector_arrays(i).azim = 0; end + sqw_copy = sqw_copy.change_header(expinf1); + assertFalse(equal_to_tol(sqw_copy.experiment_info, sqw_obj.experiment_info)); + + % (3) change entire experiment_info sqw_copy = sqw_copy.change_header(Experiment()); - sqw_copy.detpar.azim(1:10) = 0; + assertFalse(equal_to_tol(sqw_copy.experiment_info, sqw_obj.experiment_info)); + + % (4) change data arrays sqw_copy.data.dax = [2 1]; sqw_copy.data.pix.signal = 1; - - % changed data is not mirrored in initial - assertFalse(equal_to_tol(sqw_copy.main_header, sqw_obj.main_header)); - assertFalse(equal_to_tol(sqw_copy.experiment_info, sqw_obj.experiment_info)); - assertFalse(equal_to_tol(sqw_copy.detpar, sqw_obj.detpar)); assertFalse(equal_to_tol(sqw_copy.data, sqw_obj.data)); assertFalse(equal_to_tol(sqw_copy.data.pix, sqw_obj.data.pix)); + % detpar is now empty struct array, should be unchanged in the + % copy + assertTrue(equal_to_tol(sqw_copy.detpar, sqw_obj.detpar)); + + % (5) check entire sqw assertFalse(equal_to_tol(sqw_copy, sqw_obj)); end diff --git a/_test/test_sqw_class/test_sqw_copy.m b/_test/test_sqw_class/test_sqw_copy.m index 5d59cca6c2..c200fd1067 100644 --- a/_test/test_sqw_class/test_sqw_copy.m +++ b/_test/test_sqw_class/test_sqw_copy.m @@ -28,19 +28,36 @@ function test_copy_returns_distinct_object(obj) sqw_copy = copy(sqw_obj); sqw_copy.main_header.title = 'test_copy'; - sqw_copy = sqw_copy.change_header(struct([])); + assertFalse(equal_to_tol(sqw_copy.main_header, sqw_obj.main_header)); + + % want to test expinfo/instruments and expinfo/detpar + % separately, so make 2 copies of expinfo + expinf1 = sqw_copy.experiment_info; + expinf2 = sqw_copy.experiment_info; + + % all instruments changed with new name to make expinf different + for i=1:numel(expinf1.instruments), expinf1.instruments{i}.name = 'copy'; end + sqw_copy = sqw_copy.change_header(expinf1); + assertFalse(equal_to_tol(sqw_copy.experiment_info, sqw_obj.experiment_info)); + + % all detpar detectors changed with new azim value to make expinf different + for i=1:numel(expinf2.detector_arrays), expinf2.detector_arrays(i).azim = 0; end + sqw_copy = sqw_copy.change_header(expinf2); + assertFalse(equal_to_tol(sqw_copy.my_detpar(), sqw_obj.my_detpar())); + %{ + % old version of detpar change with separate detpar left for + % reference to see what was intended dtp = sqw_copy.my_detpar(); dtp.azim(1:10) = 0; sqw_copy = sqw_copy.change_detpar(dtp); - sqw_copy.data.dax = [2, 1]; - sqw_copy.data.pix.signal = 1; + %} % changed data is not mirrored in initial - assertFalse(equal_to_tol(sqw_copy.main_header, sqw_obj.main_header)); - assertFalse(equal_to_tol(sqw_copy.experiment_info, sqw_obj.experiment_info)); - assertFalse(equal_to_tol(sqw_copy.my_detpar(), sqw_obj.my_detpar())); + sqw_copy.data.dax = [2, 1]; + sqw_copy.data.pix.signal = 1; assertFalse(equal_to_tol(sqw_copy.data, sqw_obj.data)); assertFalse(equal_to_tol(sqw_copy.data.pix, sqw_obj.data.pix)); + end function test_copy_excluding_pix_returns_empty_pix_data(obj) diff --git a/horace_core/algorithms/head_horace.m b/horace_core/algorithms/head_horace.m index 1940852814..e6277c4117 100644 --- a/horace_core/algorithms/head_horace.m +++ b/horace_core/algorithms/head_horace.m @@ -108,6 +108,7 @@ [data.main_header,exper_block,data.detpar,data.data] = ... loaders{i}.get_sqw('-legacy','-nopix','-verbatim'); data.header = exper_block.header; + data.detpar = exper_block.detpar; else data = loaders{i}.get_data('-verbatim','-nopix'); if isa(data,'data_sqw_dnd') diff --git a/horace_core/sqw/@Experiment/Experiment.m b/horace_core/sqw/@Experiment/Experiment.m index d083b6dcb1..4598e7d09a 100644 --- a/horace_core/sqw/@Experiment/Experiment.m +++ b/horace_core/sqw/@Experiment/Experiment.m @@ -3,7 +3,7 @@ properties(Access=private) instruments_ = {}; %IX_inst.empty; - detector_arrays_ = [] + detector_arrays_ = IX_detector_array.empty; samples_ = {}; % IX_samp.empty; expdata_ = IX_experiment(); end @@ -19,6 +19,7 @@ properties(Dependent,Hidden) % property providing compatibility with old header interface header + detpar end properties(Constant,Access=private) fields_to_save_ = {'instruments','detector_arrays','samples','expdata'}; @@ -90,6 +91,10 @@ 'Must give all of detector_array, instrument and sample or the structure representing them') end end + + function add_detector_array(obj,detarr) + obj.detector_arrays_(end+1) = detarr; + end % function oldhdrs = convert_to_old_headers(obj,header_num) % convert Experiment into the structure suitable to be @@ -295,6 +300,15 @@ head = [head{:}]; head = rmfield(head,{'instrument','sample'}); end + + function dp = get.detpar(obj) + if numel(obj.detector_arrays)>0 + dp = obj.detector_arrays(1); + dp = dp.convert_to_old_detpar(); + else + dp = struct(); + end + end end methods(Access=protected) %------------------------------------------------------------------ diff --git a/horace_core/sqw/@Experiment/private/build_from_old_headers_.m b/horace_core/sqw/@Experiment/private/build_from_old_headers_.m index 75c1fae9c1..e8395919e5 100644 --- a/horace_core/sqw/@Experiment/private/build_from_old_headers_.m +++ b/horace_core/sqw/@Experiment/private/build_from_old_headers_.m @@ -106,7 +106,7 @@ % Construct the new header Experiment object % update with expdata, which maybe should go in the Experiment constructor -obj = Experiment([], instruments, samples); +obj = Experiment(IX_detector_array.empty, instruments, samples); obj.expdata = expdata; end % function build_from_old_headers diff --git a/horace_core/sqw/@rundatah/private/calc_sqw_.m b/horace_core/sqw/@rundatah/private/calc_sqw_.m index a4491b19ab..b63612b7ea 100644 --- a/horace_core/sqw/@rundatah/private/calc_sqw_.m +++ b/horace_core/sqw/@rundatah/private/calc_sqw_.m @@ -138,7 +138,8 @@ % ---------------------------------------------------------------------- d.main_header=main_header; d.experiment_info=header; -d.detpar=det0; +d.experiment_info.detector_arrays(end+1) = IX_detector_array(det0); +d.detpar=struct([]); d.data=data_sqw_dnd(sqw_datstr); d.runid_map = containers.Map(id,1); @@ -193,7 +194,7 @@ else sample = obj.sample; end -header = Experiment([],obj.instrument,sample); +header = Experiment(IX_detector_array.empty,instrument,sample); uoffset = [0;0;0;0]; diff --git a/horace_core/sqw/@sqw/spe.m b/horace_core/sqw/@sqw/spe.m index ed582b1c4b..748788e608 100644 --- a/horace_core/sqw/@sqw/spe.m +++ b/horace_core/sqw/@sqw/spe.m @@ -51,7 +51,7 @@ else ne=numel(w.experiment_info.en)-1; % number of energy bins end -ndet0=numel(w.detpar.group);% number of detectors +ndet0=numel(w.experiment_info.detector_arrays(1).id);% number of detectors tmp=w.data.pix.get_data({'detector_idx', 'energy_idx', 'signal', 'variance'})'; tmp=sortrows(tmp,[1,2]); % order by detector group number, then energy @@ -63,7 +63,7 @@ end % Get the indexing of detector group in the detector information -[lia,ind]=ismember(group,w.detpar.group); +[lia,ind]=ismember(group,w.experiment_info.detector_arrays(1).id); signal=NaN(ne,ndet0); err=zeros(ne,ndet0); diff --git a/horace_core/sqw/@sqw/sqw.m b/horace_core/sqw/@sqw/sqw.m index 24d0460f86..3f8526e263 100644 --- a/horace_core/sqw/@sqw/sqw.m +++ b/horace_core/sqw/@sqw/sqw.m @@ -97,7 +97,11 @@ wout = noisify(w,varargin); function dtp = my_detpar(obj) - dtp = obj.detpar_x; + if ~isempty(obj.detpar_x) + dtp = obj.detpar_x; + elseif ~isempty(obj.experiment_info.detector_arrays) + dtp = obj.experiment_info.detector_arrays(1).convert_to_old_detpar(); + end end function obj = change_detpar(obj,dtp) @@ -258,8 +262,19 @@ methods(Static) function obj = loadobj(S) - % boilerplate loadobj method, calling generic method of + % modified boilerplate loadobj method, calling generic method of % saveable class + % the generic code is preceded by the conversion of old-style + % detpar info into the new detector arrays. This allows loading + % of .mat files without converting them on disk.. + if isfield(S,'experiment_info') && isfield(S,'detpar') + if ~isempty(S.detpar) && isempty(S.experiment_info.detector_arrays) + dd = IX_detector_array(S.detpar); + S.experiment_info.detector_arrays = IX_detector_array.empty; + S.experiment_info.detector_arrays(end+1) = dd; + S.detpar = struct([]); + end + end obj = sqw(); obj = loadobj@serializable(S,obj); end @@ -275,6 +290,8 @@ % take the inputs for a cut and return them in a standard form [proj, pbin, opt,args] = process_and_validate_cut_inputs(obj, ndims_source, return_cut, varargin); + + function obj = from_old_struct(obj,S) % restore object from the old structure, which describes the % previous version of the object. @@ -307,6 +324,18 @@ end ss = rmfield(ss,'header'); end + + % assuming that detpar exists and is not empty, convert + % its contents into a detector array. There are test + % datasets with empty detpar and those should be left + % unconverted, they seem to be testing other items in + % the sqw and ignoring the detpar. + if isfield(ss,'experiment_info') && isfield(ss,'detpar') && ~isempty(ss.detpar) + dd = IX_detector_array(ss.detpar); + ss.experiment_info.detector_arrays(end+1) = dd; + ss = rmfield(ss,'detpar'); + end + if isfield(ss,'data_') ss.data = ss.data_; ss = rmfield(ss,'data_'); diff --git a/horace_core/sqw/file_io/@dnd_binfile_common/dnd_binfile_common.m b/horace_core/sqw/file_io/@dnd_binfile_common/dnd_binfile_common.m index cdf89daab1..21e39d184b 100644 --- a/horace_core/sqw/file_io/@dnd_binfile_common/dnd_binfile_common.m +++ b/horace_core/sqw/file_io/@dnd_binfile_common/dnd_binfile_common.m @@ -256,7 +256,8 @@ function check_obj_initated_properly(obj) function check_error_report_fail_(obj,pos_mess) % check if error occured during io operation and throw if it does happened [mess,res] = ferror(obj.file_id_); - if res ~= 0; error('SQW_FILE_IO:io_error',... + if res ~= 0 + error('Horace:SQW_FILE_IO:io_error',... '%s -- Reason: %s',pos_mess,mess); end end diff --git a/horace_core/sqw/file_io/@sqw_binfile_common/get_sqw.m b/horace_core/sqw/file_io/@sqw_binfile_common/get_sqw.m index 63a7e48724..459c3f9b1f 100644 --- a/horace_core/sqw/file_io/@sqw_binfile_common/get_sqw.m +++ b/horace_core/sqw/file_io/@sqw_binfile_common/get_sqw.m @@ -75,7 +75,14 @@ % Get detector parameters % ----------------------- if ~(opts.head||opts.his) - sqw_struc.detpar = obj.get_detpar(); + dp = obj.get_detpar(); + if isa(headers, 'Experiment') + dp = obj.get_detpar(); + headers.detector_arrays(end+1) = IX_detector_array(dp); + sqw_struc.detpar = struct([]); + else + sqw_struc.detpar = obj.get_detpar(); + end end % Get data