Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move detpar to detector arrays #784

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _test/test_experiment/test_experiment_constructor.m
Original file line number Diff line number Diff line change
Expand Up @@ -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(~)
Expand Down
2 changes: 1 addition & 1 deletion _test/test_sqw_class/test_equal_to_tol.m
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
27 changes: 20 additions & 7 deletions _test/test_sqw_class/test_sqw_constructor.m
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment on lines +52 to 53
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to change interface in such convoluted way?
why not to do
sqw_obj.experiment_info.id and wire up this property to the array internally

assertEqual(sqw_obj.data.pix.num_pixels, 100337);
end
Expand Down Expand Up @@ -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

Expand Down
29 changes: 23 additions & 6 deletions _test/test_sqw_class/test_sqw_copy.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I really dislike inline dos/ifs like this. Definitely a personal matter.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expinf1.instruments = cellfun(@(x) setfield(x, 'name', 'copy'), expinf1.instruments, 'UniformOutput', false)

might be cleaner? Though admittedly MATLAB's verbosity in making the output a cell-array this sort of defeats that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to change the formatting. Arguable if the cellfun version is cleaner - it is very opaque and in the interests of making clear what a test does if there is the problem here I would retain the for loop.

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)
Expand Down
1 change: 1 addition & 0 deletions horace_core/algorithms/head_horace.m
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
16 changes: 15 additions & 1 deletion horace_core/sqw/@Experiment/Experiment.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -295,6 +300,15 @@
head = [head{:}];
head = rmfield(head,{'instrument','sample'});
end

function dp = get.detpar(obj)
if numel(obj.detector_arrays)>0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not equivalent to ~isempty which denotes the meaning more clearly

dp = obj.detector_arrays(1);
dp = dp.convert_to_old_detpar();
else
dp = struct();
end
end
end
methods(Access=protected)
%------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +109 to 110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would allow old interface (user would not be so keen to learn about IX_detector_array.empty as [] looks much easier ) and make the internal assignment to IX_detector_array.empty as this happen


end % function build_from_old_headers
Expand Down
5 changes: 3 additions & 2 deletions horace_core/sqw/@rundatah/private/calc_sqw_.m
Original file line number Diff line number Diff line change
Expand Up @@ -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([]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In keeping with other changes would struct.empty be better?

Comment on lines +141 to +142
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would not work without array compression and will be very inefficient, as det0 and IX_detector_array are large objects

d.data=data_sqw_dnd(sqw_datstr);
d.runid_map = containers.Map(id,1);

Expand Down Expand Up @@ -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];
Expand Down
4 changes: 2 additions & 2 deletions horace_core/sqw/@sqw/spe.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
33 changes: 31 additions & 2 deletions horace_core/sqw/@sqw/sqw.m
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@
wout = noisify(w,varargin);

function dtp = my_detpar(obj)
dtp = obj.detpar_x;
if ~isempty(obj.detpar_x)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both are empty this will error. Expected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obj in this case is a sqw (or array thereof). Guess it is conceivable that obj could be an empty array with the right type. If it errors maybe we rewrite in python?

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)
Expand Down Expand Up @@ -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')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested it and you can do

if isfield(S, {'experimental_info', 'detpar'})

Comment on lines +269 to +270
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should all this be moved to from_old_struct and loadobj left untouched? It looks like you are verifying old structure processing here, and this should not happen in normal circumstances. New structure will eventually be prevailing, leaving old data input an exception.

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([]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct.empty

end
end
obj = sqw();
obj = loadobj@serializable(S,obj);
end
Expand All @@ -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.
Expand Down Expand Up @@ -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_');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion horace_core/sqw/file_io/@sqw_binfile_common/get_sqw.m
Original file line number Diff line number Diff line change
Expand Up @@ -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([]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct.empty

else
sqw_struc.detpar = obj.get_detpar();
end
end

% Get data
Expand Down