Skip to content
Merged
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
28 changes: 26 additions & 2 deletions src/hdfmap/nexus.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,21 +308,36 @@ def nexus_default_names(self) -> tuple[dict[str, str], dict[str, str]]:
:return: {'axes name: 'hdf_path', ...}, {'signal name: 'hdf_path', ...}
"""
axes_paths, signal_paths = self.nexus_default_paths()
if len(axes_paths) != len(self.scannables_shape()):
# If file doesn't contain defaults, or defaults are the wrong shape,
# use first and last scannables
logger.warning(f"default axes dimensions({len(axes_paths)}) does not match scannables dimensions({len(self.scannables_shape())})")
return self.first_last_scannables()
axes_names = [self.datasets[path].name for path in axes_paths]
signal_names = [self.datasets[path].name for path in signal_paths]
if not self.scannables:
# If scannables not populated, return array paths
axes_dict = {name: self.arrays[name] for name in axes_names}
signal_dict = {name: self.arrays[name] for name in signal_names}
return axes_dict, signal_dict

alt_names = {
self.datasets[path].name: self.datasets[path].names
for path in axes_paths + signal_paths
}
scannable_names = list(self.scannables)

for name in axes_names:
for n, name in enumerate(axes_names):
if name not in self.scannables:
if name in self.arrays and self.datasets[self.arrays[name]].shape == self.scannables_shape():
logger.warning(f"axes '{name}' not found in scannables, appending '{name}' to scannables")
self.scannables[name] = self.arrays[name]
else:
raise KeyError(f"axes '{name}' not found in scannables")
logger.warning(
f"axes '{name}' not found in scannables, " +
f"switching to '{scannable_names[n]}'"
)
axes_names[n] = scannable_names[n]
for n, name in enumerate(signal_names):
scannable_name = next(
(alt_name for alt_name in alt_names[name] if alt_name in scannable_names),
Expand Down Expand Up @@ -489,6 +504,15 @@ def populate(self, hdf_file: h5py.File, groups=None, default_entry_only=False):
if len(self.scannables) < len(self.scannables_shape()):
logger.warning('Less scannables than most common shape dimensions, removing scannables')
self.scannables = {}
# if default axes/ signal are not available, define using scannables
if self.scannables and NX_AXES not in self.arrays:
first_names, last_names = self.first_last_scannables()
self.arrays[NX_AXES] = next(iter(first_names.values()))
self.arrays[NX_SIGNAL] = next(iter(last_names.values()))
for n, name in enumerate(first_names):
self.arrays[f"{NX_AXES}{n}"] = first_names[name]
for n, name in enumerate(last_names):
self.arrays[f"{NX_SIGNAL}{n}"] = last_names[name]
# find the NXdetector group and assign the image data
self.generate_image_data_from_nxdetector()
# finalise map with combined namespace
Expand Down
1 change: 1 addition & 0 deletions src/hdfmap/reloader_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class NexusLoader(HdfLoader):
data = hdf.eval('dataset_name_1 * 100 + 2')
string = hdf.format('my data is {dataset_name_1:.2f}')
"""
map: NexusMap

def __init__(self, nxs_filename: str, hdf_map: NexusMap | None = None):
if not hdf_map:
Expand Down
2 changes: 1 addition & 1 deletion tests/data/test_files.json

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions tests/test_edge_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,30 @@ def test_old_i16_file():
assert address == '/entry1/measurement/sum', 'expression "_sum" returns wrong address'


@only_dls_file_system
def test_very_old_i16_file():
"""file with no default plotting"""
filename = '/dls/science/groups/das/ExampleData/hdfmap_tests/i16/777777.nxs'
assert path.isfile(filename) is True, f"{filename} doesn't exist"
mymap = hdfmap.create_nexus_map(filename)

axes_names, signal_names = mymap.nexus_default_names()
assert axes_names == {'TimeFromEpoch': '/entry1/measurement/TimeFromEpoch'}
assert signal_names == {'sum': '/entry1/measurement/sum'}

assert mymap['axes'] == '/entry1/measurement/TimeFromEpoch'
assert mymap['signal'] == '/entry1/measurement/sum'

with hdfmap.hdf_loader.load_hdf(filename) as hdf:
data = mymap.get_plot_data(hdf)
assert data['xlabel'] == 'TimeFromEpoch'
assert data['ylabel'] == 'sum'
assert data['xdata'].shape == (81, )
assert data['ydata'].shape == (81,)
assert data['axes_names'] == ['TimeFromEpoch']
assert data['signal_names'] == ['sum']


@only_dls_file_system
def test_new_i16_file():
filename = '/dls/science/groups/das/ExampleData/hdfmap_tests/i16/1040323.nxs'
Expand Down Expand Up @@ -142,6 +166,36 @@ def test_msmapper_file():
a, b, c, alpha, beta, gamma = mymap.eval(hdf, 'unit_cell')
assert gamma > 1.0, 'unit cell incorrect'

# defaults in /processed but scan_fields causes scannables to use /entry0
assert len(mymap.scannables) == 36
assert mymap.scannables_shape() == (81, )
axes_paths, signal_paths = mymap.nexus_default_paths()
assert axes_paths == ['/processed/reciprocal_space/h-axis', '/processed/reciprocal_space/k-axis', '/processed/reciprocal_space/l-axis']
assert signal_paths == ['/processed/reciprocal_space/volume', '/processed/reciprocal_space/weight']
axes_names, signal_names = mymap.nexus_default_names()
assert axes_names == {'beamOK': '/entry0/roi2/beamOK'}
assert signal_names == {'theta': '/entry0/sample/transformations/theta'}

# alternative file with /entry0, /processed, /analysis{@default}
filename = '/dls/science/groups/das/ExampleData/hdfmap_tests/i16/processed/1109527_msmapper.nxs'
mymap = hdfmap.create_nexus_map(filename, default_entry_only=False)
assert len(mymap.scannables) == 23
assert mymap.scannables_shape() == (61,)
axes_paths, signal_paths = mymap.nexus_default_paths()
assert axes_paths == ['/analysis/l_axis/l']
assert signal_paths == ['/analysis/l_axis/intensity', '/analysis/l_axis/fit']
axes_names, signal_names = mymap.nexus_default_names()
assert axes_names == {'eta_fly_fly': '/entry0/instrument/eta_fly_fly/value'}
assert signal_names == {'pil_total': '/entry0/instrument/pil3_100k/pil_total', 'pil_max_y': '/entry0/instrument/pil3_100k/pil_max_y'}

# Real defaults stored in /analysis, normally overridden by scan_fields
mymap = hdfmap.create_nexus_map(filename, default_entry_only=True)
assert len(mymap.scannables) == 3
assert mymap.scannables_shape() == (1185,)
axes_names, signal_names = mymap.nexus_default_names()
assert axes_names == {'l': '/analysis/l_axis/l'}
assert signal_names == {'intensity': '/analysis/l_axis/intensity', 'fit': '/analysis/l_axis/fit'}


@only_dls_file_system
def test_alternate_name_local_data():
Expand Down
Loading