Skip to content

Commit 37bf588

Browse files
committed
BUG: Fixed metadata extraction and tests
1 parent f9663df commit 37bf588

11 files changed

Lines changed: 99 additions & 30 deletions

File tree

SurfaceTopography/IO/DATX.py

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,51 @@ def recursive_replace(raw_metadata, context):
150150
def channels(self):
151151
instrument_info = {"vendor": "Zygo"}
152152

153-
def find_key(d, target_key):
154-
if target_key in d:
155-
return d[target_key]
156-
for v in d.values():
157-
if isinstance(v, dict):
158-
res = find_key(v, target_key)
159-
if res is not None:
160-
return res
161-
return None
162-
163-
name = find_key(self._metadata, "Instrument")
164-
if name is not None:
165-
instrument_info["name"] = str(name)
166-
serial = find_key(self._metadata, "System Serial Number")
167-
if serial is not None:
168-
instrument_info["serial"] = str(serial)
153+
fobj = self._fobj
154+
if callable(fobj):
155+
fobj = fobj()
156+
with h5py.File(fobj, 'r') as h5:
157+
def find_attr(obj, target_key):
158+
for key, val in obj.attrs.items():
159+
if key.endswith(target_key):
160+
if isinstance(val, np.ndarray) and len(val) > 0:
161+
return val[0]
162+
return val
163+
return None
164+
165+
# Search in the attributes of the group containing the surface data
166+
# or any parent group.
167+
node = h5[self._surface_path]
168+
serial = None
169+
instrument_name = None
170+
while node.name != '/':
171+
serial = find_attr(node, 'System Serial Number')
172+
instrument_name = find_attr(node, 'System Type')
173+
if serial is not None and instrument_name is not None:
174+
break
175+
node = node.parent
176+
177+
# If not found yet, also check if there is an Attributes group
178+
if serial is None or instrument_name is None:
179+
def visit_func(name, obj):
180+
nonlocal serial, instrument_name
181+
if serial is None:
182+
serial = find_attr(obj, 'System Serial Number')
183+
if instrument_name is None:
184+
instrument_name = find_attr(obj, 'System Type')
185+
186+
if 'Attributes' in h5:
187+
h5['Attributes'].visititems(visit_func)
188+
189+
if serial is not None:
190+
if isinstance(serial, bytes):
191+
serial = serial.decode('utf-8')
192+
instrument_info["serial"] = str(serial)
193+
194+
if instrument_name is not None:
195+
if isinstance(instrument_name, bytes):
196+
instrument_name = instrument_name.decode('utf-8')
197+
instrument_info["name"] = str(instrument_name)
169198

170199
return [
171200
ChannelInfo(
@@ -205,6 +234,7 @@ def topography(self, channel_index=None, physical_sizes=None, height_scale_facto
205234

206235
_info = self.channels[channel_index].info.copy()
207236
_info.update(info)
237+
_info.update({'instrument': self.channels[channel_index].info['instrument']})
208238

209239
fobj = self._fobj
210240
if callable(fobj):

SurfaceTopography/IO/DI.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ def __init__(self, file_path):
221221
"name": equipment["description"],
222222
"vendor": "Bruker",
223223
}
224+
else:
225+
info["instrument"] = {"vendor": "Bruker"}
226+
227+
if "serial number" in scanner:
228+
info["instrument"]["serial"] = scanner["serial number"]
229+
230+
for n, p in parameters:
231+
if n == "file list":
232+
if "version" in p:
233+
info["instrument"]["software"] = p["version"]
224234

225235
self._channels += [
226236
ChannelInfo(

SurfaceTopography/IO/IBW.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,30 @@ def decode_unit_entry(u):
118118
# of dimension d = sfA[d]*e + sfB[d]. sfB is left out here, because we
119119
# are interested in the width and height, not the absolute offsets.
120120

121+
#
122+
# Build instrument information
123+
#
124+
instrument_info = {"vendor": "Asylum Research"}
125+
try:
126+
note = self.data["note"].decode("latin-1")
127+
for line in note.splitlines():
128+
if ":" in line:
129+
key, value = line.split(":", 1)
130+
key = key.strip()
131+
value = value.strip()
132+
if key == "MicroscopeModel":
133+
instrument_info["name"] = value
134+
elif key == "IgorFileVersion":
135+
instrument_info["software"] = f"Igor Pro {value}"
136+
elif key == "Version":
137+
# This seems to be the software version of the Asylum software
138+
if "software" in instrument_info:
139+
instrument_info["software"] += f" (Asylum {value})"
140+
else:
141+
instrument_info["software"] = f"Asylum {value}"
142+
except (KeyError, UnicodeDecodeError):
143+
pass
144+
121145
#
122146
# Build channel information
123147
#
@@ -132,7 +156,7 @@ def decode_unit_entry(u):
132156
unit=self._data_unit,
133157
height_scale_factor=1,
134158
uniform=True,
135-
info={"instrument": {"vendor": "Asylum Research"}},
159+
info={"instrument": instrument_info},
136160
)
137161
for i, cn in enumerate(self._channel_names)
138162
]
@@ -174,7 +198,11 @@ def topography(self, channel_index=None, channel_id=None,
174198
else:
175199
raise MetadataAlreadyFixedByFile('physical_sizes')
176200

177-
topo = Topography(height_data, physical_sizes, unit=unit, info=info, periodic=periodic)
201+
_info = self.channels[channel_index].info.copy()
202+
_info.update(info)
203+
_info.update({'instrument': self.channels[channel_index].info['instrument']})
204+
205+
topo = Topography(height_data, physical_sizes, unit=unit, info=_info, periodic=periodic)
178206
# we could pass the data units here, but they dont seem to be always
179207
# correct for all channels?!
180208

SurfaceTopography/IO/JPK.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,12 @@ def __init__(self, file_path):
296296
unit=default_slot_unit,
297297
info={
298298
"acquisition_time": acquisition_time,
299-
"instrument": {"vendor": "JPK Instruments"},
299+
"instrument": {
300+
"vendor": "JPK Instruments",
301+
"software": str(global_metadata.get("ProgramVersion"))
302+
if "ProgramVersion" in global_metadata
303+
else None,
304+
},
300305
"raw_metadata": raw_metadata,
301306
},
302307
tags={"page_index": page_index},

SurfaceTopography/IO/SUR.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,9 @@ def can_read(cls, buffer: bytes) -> MagicMatch:
199199
def channels(self):
200200
header = self._metadata.header
201201

202-
instrument_info = {}
202+
instrument_info = {"vendor": "Digital Surf"}
203203
if header.instrument_name:
204204
instrument_info["name"] = header.instrument_name
205-
# Many SUR files are generated by Digital Surf MountainsMap software
206-
# but the instrument name itself might be in the header.
207-
if "DIGITAL SURF" in header.instrument_name:
208-
instrument_info["vendor"] = "Digital Surf"
209205

210206
info = {"instrument": instrument_info, "raw_metadata": header}
211207

test/IO/test_nc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
import numpy as np
2929
import pytest
30-
from muGrid import FFTEngine
30+
from muGrid.Wrappers import FFTEngine
3131
from NuMPI import MPI
3232
from scipy.io import netcdf_file
3333

test/IO/test_npy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import NuMPI
3232
import numpy as np
3333
import pytest
34-
from muGrid import FFTEngine
34+
from muGrid.Wrappers import FFTEngine
3535
from NuMPI import MPI
3636

3737
from SurfaceTopography import open_topography

test/test_make_sphere.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#
2525

2626
import numpy as np
27-
from muGrid import FFTEngine
27+
from muGrid.Wrappers import FFTEngine
2828

2929
from SurfaceTopography import make_sphere
3030

test/test_make_topography_from_function.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
# SOFTWARE.
2323
#
2424
import numpy as np
25-
from muGrid import FFTEngine
25+
from muGrid.Wrappers import FFTEngine
2626

2727
from SurfaceTopography.Special import make_topography_from_function
2828

test/test_scalar_parameters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
import numpy as np
3131
import pytest
32-
from muGrid import FFTEngine
32+
from muGrid.Wrappers import FFTEngine
3333
from NuMPI import MPI
3434

3535
from SurfaceTopography import (

0 commit comments

Comments
 (0)