hdfmap 0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hdfmap/__init__.py +76 -0
- hdfmap/eval_functions.py +172 -0
- hdfmap/file_functions.py +210 -0
- hdfmap/hdfmap_class.py +656 -0
- hdfmap/logging.py +40 -0
- hdfmap/nexus.py +240 -0
- hdfmap/reloader_class.py +140 -0
- hdfmap-0.4.dist-info/LICENSE +201 -0
- hdfmap-0.4.dist-info/METADATA +476 -0
- hdfmap-0.4.dist-info/RECORD +12 -0
- hdfmap-0.4.dist-info/WHEEL +5 -0
- hdfmap-0.4.dist-info/top_level.txt +1 -0
hdfmap/__init__.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hdfmap
|
|
3
|
+
Map objects within an HDF file and create a dataset namespace.
|
|
4
|
+
|
|
5
|
+
--- Usage ---
|
|
6
|
+
# HdfMap from NeXus file:
|
|
7
|
+
from hdfmap import create_nexus_map, load_hdf
|
|
8
|
+
hmap = create_nexus_map('file.nxs')
|
|
9
|
+
with load_hdf('file.nxs') as nxs:
|
|
10
|
+
address = hmap.get_address('energy')
|
|
11
|
+
energy = nxs[address][()]
|
|
12
|
+
string = hmap.format_hdf(nxs, "the energy is {energy:.2f} keV")
|
|
13
|
+
d = hmap.get_data_block(nxs) # classic data table, d.scannable, d.metadata
|
|
14
|
+
|
|
15
|
+
# Shortcuts - single file reloading class
|
|
16
|
+
from hdfmap import NexusLoader
|
|
17
|
+
scan = NexusLoader('file.nxs')
|
|
18
|
+
[data1, data2] = scan.get_data(['dataset_name_1', 'dataset_name_2'])
|
|
19
|
+
data = scan.eval('dataset_name_1 * 100 + 2')
|
|
20
|
+
string = scan.format('my data is {dataset_name_1:.2f}')
|
|
21
|
+
|
|
22
|
+
# Shortcuts - multifile load data
|
|
23
|
+
from hdfmap import hdf_data, hdf_eval, hdf_format, hdf_image
|
|
24
|
+
all_data = hdf_data([f"file{n}.nxs" for n in range(100)], 'dataset_name')
|
|
25
|
+
normalised_data = hdf_eval(filenames, 'total / Transmission / (rc / 300.)')
|
|
26
|
+
descriptions = hdf_eval(filenames, 'Energy: {en:5.3f} keV')
|
|
27
|
+
image = hdf_image(filenames, index=31)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
By Dr Dan Porter
|
|
31
|
+
Diamond Light Source Ltd
|
|
32
|
+
2024
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from .logging import set_all_logging_level
|
|
36
|
+
from .hdfmap_class import HdfMap
|
|
37
|
+
from .nexus import NexusMap
|
|
38
|
+
from .file_functions import list_files, load_hdf, create_hdf_map, create_nexus_map
|
|
39
|
+
from .file_functions import hdf_data, hdf_image, hdf_eval, hdf_format, nexus_data_block
|
|
40
|
+
from .reloader_class import HdfLoader, NexusLoader
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
HdfMap, NexusMap, load_hdf, create_hdf_map, create_nexus_map,
|
|
45
|
+
hdf_data, hdf_image, hdf_eval, hdf_format, nexus_data_block, HdfLoader, NexusLoader,
|
|
46
|
+
set_all_logging_level
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
__version__ = "0.4.0"
|
|
50
|
+
__date__ = "2024/08/16"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def version_info() -> str:
|
|
54
|
+
return 'hdfmap version %s (%s)' % (__version__, __date__)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def module_info() -> str:
|
|
58
|
+
import sys
|
|
59
|
+
out = 'Python version %s' % sys.version
|
|
60
|
+
out += '\n at: %s' % sys.executable
|
|
61
|
+
out += '\n %s: %s' % (version_info(), __file__)
|
|
62
|
+
# Modules
|
|
63
|
+
import numpy
|
|
64
|
+
out += '\n numpy version: %s' % numpy.__version__
|
|
65
|
+
import h5py
|
|
66
|
+
out += '\n h5py version: %s' % h5py.__version__
|
|
67
|
+
# import imageio
|
|
68
|
+
# out += '\n imageio version: %s' % imageio.__version__
|
|
69
|
+
try:
|
|
70
|
+
import hdf5plugin
|
|
71
|
+
out += '\n hdf5plugin: %s' % hdf5plugin.version
|
|
72
|
+
except ImportError:
|
|
73
|
+
out += '\n hdf5plugin: None'
|
|
74
|
+
import os
|
|
75
|
+
out += '\nRunning in directory: %s\n' % os.path.abspath('.')
|
|
76
|
+
return out
|
hdfmap/eval_functions.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
hdf eval functions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import ast
|
|
7
|
+
import builtins
|
|
8
|
+
import datetime
|
|
9
|
+
import re
|
|
10
|
+
import typing
|
|
11
|
+
import numpy as np
|
|
12
|
+
import h5py
|
|
13
|
+
|
|
14
|
+
from .logging import create_logger
|
|
15
|
+
|
|
16
|
+
# parameters
|
|
17
|
+
GLOBALS = {'np': np}
|
|
18
|
+
GLOBALS_NAMELIST = dir(builtins) + list(GLOBALS.keys())
|
|
19
|
+
logger = create_logger(__name__)
|
|
20
|
+
# regex patterns
|
|
21
|
+
special_characters = re.compile(r'\W') # finds all special non-alphanumberic characters
|
|
22
|
+
long_floats = re.compile(r'\d+\.\d{5,}') # finds floats with long trailing decimals
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def expression_safe_name(string: str, replace: str = '_') -> str:
|
|
26
|
+
"""
|
|
27
|
+
Returns an expression safe name
|
|
28
|
+
:param string: any string
|
|
29
|
+
:param replace: str replace special characters with this
|
|
30
|
+
:return: string with special characters replaced
|
|
31
|
+
"""
|
|
32
|
+
return special_characters.sub('_', string)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def round_string_floats(string):
|
|
36
|
+
"""
|
|
37
|
+
Shorten string by removing long floats
|
|
38
|
+
:param string: string, e.g. '#810002 scan eta 74.89533603616637 76.49533603616636 0.02 pil3_100k 1 roi2'
|
|
39
|
+
:return: shorter string, e.g. '#810002 scan eta 74.895 76.495 0.02 pil3_100k 1 roi2'
|
|
40
|
+
"""
|
|
41
|
+
def subfun(m):
|
|
42
|
+
return str(round(float(m.group()), 3))
|
|
43
|
+
return long_floats.sub(subfun, string)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def dataset2data(dataset: h5py.Dataset, index: int | slice = (), direct_load=False) -> datetime.datetime | str | np.ndarray:
|
|
47
|
+
"""
|
|
48
|
+
Read the data from a h5py Dataset and convert to either datetime, str or squeezed numpy array
|
|
49
|
+
:param dataset: h5py.Dataset containing data
|
|
50
|
+
:param index: index of array (not used if dataset is string/ bytes type)
|
|
51
|
+
:param direct_load: loads the data directly without conversion if True
|
|
52
|
+
:return datetime.datetime: if data is an isoformat string, returns datetime object
|
|
53
|
+
:return str: if data is another string, returns str with long floats rounded
|
|
54
|
+
:return np.ndarray: if data is another numeric object
|
|
55
|
+
"""
|
|
56
|
+
if direct_load:
|
|
57
|
+
return dataset[index]
|
|
58
|
+
if np.issubdtype(dataset, np.number):
|
|
59
|
+
return np.squeeze(dataset[index]) # numeric np.ndarray
|
|
60
|
+
try:
|
|
61
|
+
# timestamp -> datetime64 -> datetime
|
|
62
|
+
timestamp = np.squeeze(dataset[index]).astype(np.datetime64).astype(datetime.datetime)
|
|
63
|
+
# single datetime obj vs array of datetime obj
|
|
64
|
+
return timestamp[()] if timestamp.ndim == 0 else timestamp
|
|
65
|
+
except ValueError:
|
|
66
|
+
try:
|
|
67
|
+
string_dataset = dataset.asstr()[()]
|
|
68
|
+
if dataset.ndim == 0:
|
|
69
|
+
return round_string_floats(string_dataset) # bytes or str -> str
|
|
70
|
+
return string_dataset # str array
|
|
71
|
+
except ValueError:
|
|
72
|
+
return np.squeeze(dataset[index]) # other np.ndarray
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def check_unsafe_eval(eval_str: str) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Check str for naughty eval arguments such as sys, os or import
|
|
78
|
+
This is not foolproof.
|
|
79
|
+
:param eval_str: str
|
|
80
|
+
:return: pass or raise error
|
|
81
|
+
"""
|
|
82
|
+
bad_names = ['import', 'os.', 'sys.', 'open(', 'eval', 'exec']
|
|
83
|
+
for bad in bad_names:
|
|
84
|
+
if bad in eval_str:
|
|
85
|
+
raise Exception('This operation is not allowed as it contains: "%s"' % bad)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def find_identifiers(expression: str) -> list[str]:
|
|
89
|
+
"""Returns list of variable names in expression, ommiting builtins and globals"""
|
|
90
|
+
# varnames = re.findall(r'[a-zA-Z]\w*', expression)
|
|
91
|
+
return [node.id for node in ast.walk(ast.parse(expression, mode='eval'))
|
|
92
|
+
if type(node) is ast.Name and node.id not in GLOBALS_NAMELIST]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def extra_hdf_data(hdf_file: h5py.File) -> dict:
|
|
96
|
+
"""Extract filename, filepath and other additional data fom hdf file"""
|
|
97
|
+
filepath = getattr(hdf_file, 'filename', 'unknown')
|
|
98
|
+
return {
|
|
99
|
+
'filepath': filepath,
|
|
100
|
+
'filename': os.path.basename(filepath),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def generate_namespace(hdf_file: h5py.File, hdf_namespace: dict[str, str], identifiers: list[str] | None = None,
|
|
105
|
+
default: typing.Any = np.array('--')) -> dict[str, typing.Any]:
|
|
106
|
+
"""
|
|
107
|
+
Generate namespace dict - create a dictionary linking the name of a dataset to the dataset value
|
|
108
|
+
|
|
109
|
+
Adds additional values if not in name_path dict:
|
|
110
|
+
filename: str, name of hdf_file
|
|
111
|
+
filepath: str, full path of hdf_file
|
|
112
|
+
_*name*: str hdf path of *name*
|
|
113
|
+
|
|
114
|
+
:param hdf_file: h5py.File object
|
|
115
|
+
:param hdf_namespace: locations of data in hdf file, dict[identifier]='/hdf/dataset/path'
|
|
116
|
+
:param identifiers: list of names to load from hdf_file, if None, use generate all items in name_path
|
|
117
|
+
:param default: any, if varname not in name_path - return default instead
|
|
118
|
+
:return: dict {'name': value, '_name': '/hdf/path'}
|
|
119
|
+
"""
|
|
120
|
+
if identifiers is None:
|
|
121
|
+
identifiers = list(hdf_namespace.keys())
|
|
122
|
+
# TODO: add ROI commands e.g. nroi[1,2,3,4] -> default_image([1,2,3,4])
|
|
123
|
+
# TODO: add name@attribute e.g. incident_energy@units -> 'eV'
|
|
124
|
+
# TODO: add name.label e.g. axes.label -> 'eta [Deg]'
|
|
125
|
+
namespace = {
|
|
126
|
+
name: dataset2data(hdf_file[hdf_namespace[name]])
|
|
127
|
+
for name in identifiers if name in hdf_namespace and hdf_namespace[name] in hdf_file
|
|
128
|
+
}
|
|
129
|
+
defaults = {
|
|
130
|
+
name: default
|
|
131
|
+
for name in identifiers if (name not in hdf_namespace) or (hdf_namespace[name] not in hdf_file)
|
|
132
|
+
}
|
|
133
|
+
hdf_paths = {name: hdf_namespace[name[1:]] for name in identifiers
|
|
134
|
+
if name.startswith('_') and name[1:] in hdf_namespace}
|
|
135
|
+
# add extra params
|
|
136
|
+
extras = extra_hdf_data(hdf_file)
|
|
137
|
+
return {**defaults, **extras, **hdf_paths, **namespace}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def eval_hdf(hdf_file: h5py.File, expression: str, hdf_namespace: dict[str, str]) -> typing.Any:
|
|
141
|
+
"""
|
|
142
|
+
Evaluate an expression using the namespace of the hdf file
|
|
143
|
+
:param hdf_file: h5py.File object
|
|
144
|
+
:param expression: str expression to be evaluated
|
|
145
|
+
:param hdf_namespace: dict of {'variable name': '/hdf/dataset/path'}
|
|
146
|
+
:return: eval(expression)
|
|
147
|
+
"""
|
|
148
|
+
if expression in hdf_file:
|
|
149
|
+
return dataset2data(hdf_file[expression])
|
|
150
|
+
check_unsafe_eval(expression)
|
|
151
|
+
# find identifiers matching names in the namespace
|
|
152
|
+
identifiers = [name for name in hdf_namespace if name in special_characters.split(expression)]
|
|
153
|
+
# find other non-builtin identifiers
|
|
154
|
+
identifiers += [name for name in find_identifiers(expression) if name not in identifiers]
|
|
155
|
+
namespace = generate_namespace(hdf_file, hdf_namespace, identifiers)
|
|
156
|
+
logger.info(f"Expression: {expression}\nidentifiers: {identifiers}\n")
|
|
157
|
+
logger.debug(f"namespace: {namespace}\n")
|
|
158
|
+
return eval(expression, GLOBALS, namespace)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def format_hdf(hdf_file: h5py.File, expression: str, hdf_namespace: dict[str, str]) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Evaluate a formatted string expression using the namespace of the hdf file
|
|
164
|
+
:param hdf_file: h5py.File object
|
|
165
|
+
:param expression: str expression using {name} format specifiers
|
|
166
|
+
:param hdf_namespace: dict of {'variable name': '/hdf/dataset/path'}
|
|
167
|
+
:return: eval_hdf(f"expression")
|
|
168
|
+
"""
|
|
169
|
+
expression = 'f"""' + expression + '"""' # convert to fstr
|
|
170
|
+
return eval_hdf(hdf_file, expression, hdf_namespace)
|
|
171
|
+
|
|
172
|
+
|
hdfmap/file_functions.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import h5py
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from .logging import create_logger
|
|
6
|
+
from .hdfmap_class import HdfMap
|
|
7
|
+
from .nexus import NexusMap
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
EXTENSIONS = ['.nxs', '.hdf', '.hdf5', '.h5']
|
|
11
|
+
DEFAULT_EXTENSION = EXTENSIONS[0]
|
|
12
|
+
DEFAULT_HDF_PATH = "entry1/scan_command"
|
|
13
|
+
logger = create_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_hdf(hdf_filename: str) -> h5py.File:
|
|
17
|
+
"""Load hdf file, return h5py.File object"""
|
|
18
|
+
return h5py.File(hdf_filename, 'r')
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def list_files(folder_directory: str, extension=DEFAULT_EXTENSION) -> list[str]:
|
|
22
|
+
"""Return list of files in directory with extension, returning list of full file paths"""
|
|
23
|
+
try:
|
|
24
|
+
return sorted(
|
|
25
|
+
(file.path for file in os.scandir(folder_directory) if file.is_file() and file.name.endswith(extension)),
|
|
26
|
+
key=lambda x: os.path.getmtime(x)
|
|
27
|
+
)
|
|
28
|
+
except FileNotFoundError:
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def as_str_list(string: str | list[str]) -> list[str]:
|
|
33
|
+
"""
|
|
34
|
+
Helper function to convert str or list of str to list of str
|
|
35
|
+
:param string: str, byteString, list, array
|
|
36
|
+
:return: list of str
|
|
37
|
+
"""
|
|
38
|
+
return list(np.asarray(string, dtype=str).reshape(-1))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_hdf_map(hdf_filename: str) -> HdfMap:
|
|
42
|
+
"""
|
|
43
|
+
Create a HdfMap from a hdf file
|
|
44
|
+
:param hdf_filename: str filename of hdf file
|
|
45
|
+
:return: HdfMap
|
|
46
|
+
"""
|
|
47
|
+
with load_hdf(hdf_filename) as hdf:
|
|
48
|
+
hdf_map = HdfMap(hdf)
|
|
49
|
+
return hdf_map
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def create_nexus_map(hdf_filename: str, groups: None | list[str] = None,
|
|
53
|
+
default_entry_only: bool = False) -> NexusMap:
|
|
54
|
+
"""
|
|
55
|
+
Create a HdfMap from a NeXus file, loading default parameters and allowing a reduced, single entry map
|
|
56
|
+
:param hdf_filename: str filename of hdf file
|
|
57
|
+
:param groups: list of groups to collect datasets from
|
|
58
|
+
:param default_entry_only: if True, only the first or default entry will be loaded
|
|
59
|
+
:return: NexusMap
|
|
60
|
+
"""
|
|
61
|
+
hdf_map = NexusMap()
|
|
62
|
+
with load_hdf(hdf_filename) as hdf:
|
|
63
|
+
hdf_map.populate(hdf, groups=groups, default_entry_only=default_entry_only)
|
|
64
|
+
if not hdf_map.scannables:
|
|
65
|
+
print('NXdata not found, getting scannables from most common array size')
|
|
66
|
+
size = hdf_map.most_common_size()
|
|
67
|
+
hdf_map.generate_scannables(size)
|
|
68
|
+
return hdf_map
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def hdf_data(filenames: str | list[str], name_or_path: str | list[str], hdf_map: HdfMap = None,
|
|
72
|
+
index=(), default=None, fixed_output=False):
|
|
73
|
+
"""
|
|
74
|
+
General purpose function to retrieve data from HDF files
|
|
75
|
+
:param filenames: str or list of str - file paths
|
|
76
|
+
:param name_or_path: str or list of str - names or paths of HDF datasets
|
|
77
|
+
:param hdf_map: HdfMap object, or None to generate from first file
|
|
78
|
+
:param index: dataset index or slice
|
|
79
|
+
:param default: value to give if dataset doesn't exist in file
|
|
80
|
+
:param fixed_output: if True, always returns list of list
|
|
81
|
+
:return if single file, single dataset: single value
|
|
82
|
+
:return if multi file or multi dataset: list, len(filenames) or len(name_or_path)
|
|
83
|
+
:return if multi file and multi dataset: list[files: list[names]]
|
|
84
|
+
"""
|
|
85
|
+
# cast as 1D arrays
|
|
86
|
+
filenames = as_str_list(filenames)
|
|
87
|
+
name_or_path = as_str_list(name_or_path)
|
|
88
|
+
# generate hdf_map
|
|
89
|
+
if hdf_map is None:
|
|
90
|
+
hdf_map = create_hdf_map(filenames[0])
|
|
91
|
+
out = []
|
|
92
|
+
for filename in filenames:
|
|
93
|
+
logger.info(f"\nHDF file: {filename}")
|
|
94
|
+
with load_hdf(filename) as hdf:
|
|
95
|
+
out.append([hdf_map.get_data(hdf, name, index=index, default=default) for name in name_or_path])
|
|
96
|
+
if fixed_output:
|
|
97
|
+
return out
|
|
98
|
+
if len(filenames) == 1 and len(name_or_path) == 1:
|
|
99
|
+
return out[0][0]
|
|
100
|
+
if len(filenames) == 1 and len(name_or_path) > 1:
|
|
101
|
+
return out[0]
|
|
102
|
+
if len(name_or_path) == 1:
|
|
103
|
+
return [val[0] for val in out]
|
|
104
|
+
return out
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def hdf_eval(filenames: str | list[str], expression: str, hdf_map: HdfMap = None, fixed_output=False):
|
|
108
|
+
"""
|
|
109
|
+
Evaluate expression using dataset names
|
|
110
|
+
:param filenames: str or list of str - file paths
|
|
111
|
+
:param expression: str expression to evaluate in each file, e.g. "roi2_sum / Transmission"
|
|
112
|
+
:param hdf_map: HdfMap object, or None to generate from first file
|
|
113
|
+
:param fixed_output: if True, always returns list len(filenames)
|
|
114
|
+
:return if single file: single output
|
|
115
|
+
:return if multi file: list, len(filenames)
|
|
116
|
+
"""
|
|
117
|
+
# cast as 1D arrays
|
|
118
|
+
filenames = as_str_list(filenames)
|
|
119
|
+
# generate hdf_map
|
|
120
|
+
if hdf_map is None:
|
|
121
|
+
hdf_map = create_hdf_map(filenames[0])
|
|
122
|
+
out = []
|
|
123
|
+
for filename in filenames:
|
|
124
|
+
logger.info(f"\nHDF file: {filename}")
|
|
125
|
+
with load_hdf(filename) as hdf:
|
|
126
|
+
out.append(hdf_map.eval(hdf, expression))
|
|
127
|
+
if not fixed_output and len(filenames) == 1:
|
|
128
|
+
return out[0]
|
|
129
|
+
return out
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def hdf_format(filenames: str | list[str], expression: str, hdf_map: HdfMap = None, fixed_output=False):
|
|
133
|
+
"""
|
|
134
|
+
Evaluate string format expression using dataset names
|
|
135
|
+
:param filenames: str or list of str - file paths
|
|
136
|
+
:param expression: str expression to evaluate in each file, e.g. "the energy is {en:.2f} keV"
|
|
137
|
+
:param hdf_map: HdfMap object, or None to generate from first file
|
|
138
|
+
:param fixed_output: if True, always returns list len(filenames)
|
|
139
|
+
:return if single file: single output
|
|
140
|
+
:return if multi file: list, len(filenames)
|
|
141
|
+
"""
|
|
142
|
+
# cast as 1D arrays
|
|
143
|
+
filenames = as_str_list(filenames)
|
|
144
|
+
# generate hdf_map
|
|
145
|
+
if hdf_map is None:
|
|
146
|
+
hdf_map = create_hdf_map(filenames[0])
|
|
147
|
+
out = []
|
|
148
|
+
for filename in filenames:
|
|
149
|
+
logger.info(f"\nHDF file: {filename}")
|
|
150
|
+
with load_hdf(filename) as hdf:
|
|
151
|
+
out.append(hdf_map.format_hdf(hdf, expression))
|
|
152
|
+
if not fixed_output and len(filenames) == 1:
|
|
153
|
+
return out[0]
|
|
154
|
+
return out
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def hdf_image(filenames: str | list[str], index: slice = None, hdf_map: HdfMap = None, fixed_output=False):
|
|
158
|
+
"""
|
|
159
|
+
Evaluate string format expression using dataset names
|
|
160
|
+
:param filenames: str or list of str - file paths
|
|
161
|
+
:param index: index or slice of dataset volume, or None to use middle index
|
|
162
|
+
:param hdf_map: HdfMap object, or None to generate from first file
|
|
163
|
+
:param fixed_output: if True, always returns list len(filenames)
|
|
164
|
+
:return if single file: single output - numpy array
|
|
165
|
+
:return if multi file: list, len(filenames)
|
|
166
|
+
"""
|
|
167
|
+
# cast as 1D arrays
|
|
168
|
+
filenames = as_str_list(filenames)
|
|
169
|
+
# generate hdf_map
|
|
170
|
+
if hdf_map is None:
|
|
171
|
+
hdf_map = create_hdf_map(filenames[0])
|
|
172
|
+
out = []
|
|
173
|
+
for filename in filenames:
|
|
174
|
+
logger.info(f"\nHDF file: {filename}")
|
|
175
|
+
with load_hdf(filename) as hdf:
|
|
176
|
+
out.append(hdf_map.get_image(hdf, index=index))
|
|
177
|
+
if not fixed_output and len(filenames) == 1:
|
|
178
|
+
return out[0]
|
|
179
|
+
return out
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def nexus_data_block(filenames: str | list[str], hdf_map: HdfMap = None, fixed_output=False):
|
|
183
|
+
"""
|
|
184
|
+
Create classic dict like dataloader objects from nexus files
|
|
185
|
+
E.G.
|
|
186
|
+
d = nexus_data_block('filename')
|
|
187
|
+
d.scannable -> array
|
|
188
|
+
d.metadata.filename -> value
|
|
189
|
+
d.keys() -> list of items
|
|
190
|
+
|
|
191
|
+
:param filenames: str or list of str - file paths
|
|
192
|
+
:param hdf_map: HdfMap object, or None to generate from first file
|
|
193
|
+
:param fixed_output: if True, always returns list len(filenames)
|
|
194
|
+
:return if single file: single output - dict like DataObject
|
|
195
|
+
:return if multi file: list, len(filenames)
|
|
196
|
+
"""
|
|
197
|
+
# cast as 1D arrays
|
|
198
|
+
filenames = as_str_list(filenames)
|
|
199
|
+
# generate hdf_map
|
|
200
|
+
if hdf_map is None:
|
|
201
|
+
hdf_map = create_nexus_map(filenames[0])
|
|
202
|
+
out = []
|
|
203
|
+
for filename in filenames:
|
|
204
|
+
logger.info(f"\nHDF file: {filename}")
|
|
205
|
+
with load_hdf(filename) as hdf:
|
|
206
|
+
out.append(hdf_map.get_dataholder(hdf))
|
|
207
|
+
if not fixed_output and len(filenames) == 1:
|
|
208
|
+
return out[0]
|
|
209
|
+
return out
|
|
210
|
+
|