import numpy as np
from .. import errors
__all__ = ["load_txt"]
converters = {
0: lambda s: float(s.decode().strip().replace(",", ".")),
1: lambda s: float(s.decode().strip().replace(",", ".")),
2: lambda s: float(s.decode().strip().replace(",", ".")),
}
def crop_beginning(data):
"""Crop constant padding at the begin of the data column"""
assert data.shape[1] == 2
start = data[0, 1]
for ii in range(1, data.shape[0]):
if data[ii, 1] != start:
break
else:
raise ValueError("Encountered all-constant data!")
return data[ii-1:, :]
def detect_txt(path):
"""File should be plain text"""
valid = False
try:
rawdata = np.loadtxt(path, converters=converters, max_rows=10)
except (ValueError, IndexError):
pass
else:
if np.issubdtype(rawdata.dtype, np.floating):
valid = True
return valid
[docs]def load_txt(path, callback=None, meta_override=None):
"""Load text files exported by the NT-MDT Nova software
The columns are assumed to be: height (piezo) [nm],
Deflection during approach (nA), Deflection during retraction (nA).
The sensitivity in meta_override should be given in [m/A]
(even though it is displayed as [m/V]).
This loader removes constant-value padding at the beginning of
the data columns, an artifact that is sometimes introduced during
data export. There are no metadata in this file format.
Test data were provided by Yuri Efremov
:cite:`Efremov_figshare_20` :cite:`Efremov_2015`.
Note that support for the original .mdt files is currently (2020)
not possible. There exist binary readers for nt-mdt files
(https://github.com/kaitai-io/kaitai_struct/), but this does
not work for `exemplary data
<https://doi.org/10.6084/m9.figshare.11862327.v1>`_. If the
NT-MDT Nova software is not available, it should still be
possible to load the data with `Ggyddion <http://gwyddion.net>`_
and export it to something afmformats understands.
Parameters
----------
path: str or pathlib.Path or io.TextIOBase
path to an ntmdt-exported .txt file
callback: callable
function for progress tracking; must accept a float in
[0, 1] as an argument.
meta_override: dict
if specified, contains key-value pairs of metadata that
are used when loading the files
(see :data:`afmformats.meta.META_FIELDS`)
"""
if meta_override is None:
meta_override = {}
req_metadata = ["sensitivity", "spring constant"]
mis_metadata = [key for key in req_metadata if key not in meta_override]
if mis_metadata:
raise errors.MissingMetaDataError(
mis_metadata,
f"Please specify {' and '.join(mis_metadata)}!")
rawdata = np.loadtxt(path, converters=converters)
if rawdata.shape[1] != 3:
raise errors.InvalidFileFormatError(
"Expected 3 columns, got {}: {}".format(rawdata.shape[1], path))
raw_apr = crop_beginning(rawdata[:, :2])
raw_ret = crop_beginning(rawdata[:, ::2])
data = {"height (measured)": np.concatenate((raw_apr[::-1, 0],
raw_ret[:, 0])) * 1e-9}
fmult = meta_override["sensitivity"] * meta_override["spring constant"]
data["force"] = np.concatenate((raw_apr[::-1, 1],
raw_ret[:, 1])) * fmult * 1e-9
data["segment"] = np.concatenate(
(np.zeros(raw_apr.shape[0], dtype=np.uint8),
np.ones(raw_ret.shape[0], dtype=np.uint8)))
metadata = {"enum": 0,
"imaging mode": "force-distance",
"path": path,
}
metadata.update(meta_override)
dd = {"data": data,
"metadata": metadata}
if callback is not None:
callback(1)
return [dd]
recipe_ntmdt_txt = {
"descr": "exported by NT-MDT Nova",
"detect": detect_txt,
"loader": load_txt,
"suffix": ".txt",
"modalities": ["force-distance"],
"maker": "NT-MDT Spectrum Instruments",
}