Source code for afmformats.formats.fmt_chiaro_txt

import numpy as np
import pathlib

__all__ = ["load_txt"]


def parse_metadata(file_metadata):
    """Parse the raw text file metadata information."""
    file_metadata = [m for m in file_metadata if m != '\n']
    file_metadata = [_d.strip('\n').split('\t') for _d in file_metadata]

    # handle each weirdly written piece of metadata
    notes = {}
    for sublist in file_metadata:
        if len(sublist) % 2 == 0:
            even, odd = sublist[::2], sublist[1::2]
            notes.update(dict(zip(even, odd)))
        elif len(sublist) == 1:

            if sublist[0].count(":") == 1:
                notes[sublist[0].split(":")[0]] = sublist[0].split(":")[1]
                continue

            # try to split by whitespace and units within parentheses
            split_sublist = sublist[0].split(' ')
            if len(split_sublist) % 2 == 0:
                notes[split_sublist[0]] = split_sublist[1]
            elif len(split_sublist) == 1:
                # no value
                notes[sublist[0]] = sublist[0]
            else:
                if "(" in sublist[0] and ")" in sublist[0]:
                    assert "(" in split_sublist[1]  # should be the second part
                    notes[split_sublist[0] + " " + split_sublist[1]] = \
                        split_sublist[2]

    # cleanup trailing whitespaces
    for key, val in notes.items():
        notes[key] = val.strip()
    return notes


def open_check_content(path):
    # encoding found with `chardet.detect`
    with path.open(encoding='ISO-8859-1') as fd:
        txtdata = fd.readlines()

    valid = False
    ind = None
    for line in txtdata:
        if "Device:	Chiaro" in line:
            valid = True
        elif "Time (s)\tLoad" in line:
            ind = txtdata.index(line)
    if not valid:
        raise ValueError

    _metadata = txtdata[:ind]
    _columns = txtdata[ind].strip('\n').split('\t')
    _data = txtdata[ind + 1:]
    _data_split = [_d.strip('\n').split('\t') for _d in _data]
    _data = np.asarray(_data_split).astype(float)
    assert len(_columns) == _data.shape[1]

    return _metadata, _columns, _data


def detect_txt(path):
    """File should be plain text"""
    valid = False
    try:
        _metadata, _columns, _data = open_check_content(path)
    except (ValueError, IndexError):
        pass
    else:
        if np.issubdtype(_data.dtype, np.floating):
            valid = True
    return valid


[docs] def load_txt(path, callback=None, meta_override=None): """Load text files exported by the Optics11 Chiaro Indenter. Parameters ---------- path: str or pathlib.Path or io.TextIOBase path to an chiaro-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 = {} _metadata, _columns, _data = open_check_content(path) data = { "time": _data[:, 0], "force": _data[:, 1] * 1e-6, # load (uN) "height (measured)": _data[:, 2] * 1e-9, # Indentation (nm) "height (piezo)": _data[:, 4] * 1e-9, # Piezo (nm) } data["height (measured)"] *= -1 data["height (piezo)"] *= -1 cantilever = _data[:, 3] * 1e-9 # Cantilever (nm) cantilever *= -1 # we use the file tip position, as nanite computes it incorrectly data["tip position"] = data["height (piezo)"] - cantilever max_force_ind = np.argmax(data["force"]) data["segment"] = np.zeros(_data.shape[0], dtype=np.uint8) data["segment"][:max_force_ind + 1] = 0 data["segment"][max_force_ind + 1:] = 1 notes = parse_metadata(_metadata) # Metadata metadata = {} metadata["duration"] = float(np.max(data["time"])) # acquisition metadata["imaging mode"] = "force-distance" metadata["feedback mode"] = "contact" metadata["setpoint"] = float(notes["Max load (µN)"]) metadata["spring constant"] = float(notes["k (N/m)"]) metadata["z range"] = float(notes["Piezo position (nm) (Measured)"]) # dataset metadata["enum"] = 0 metadata["speed approach"] = float(notes["Piezo speed (µm/s)"]) metadata["speed retract"] = float(notes["Piezo speed (µm/s)"]) # setup metadata["instrument"] = notes["Device:"] metadata["software"] = notes["Device:"] metadata["software version"] = notes["Software version"] # storage metadata["path"] = pathlib.Path(path) metadata["date"] = notes["Date"] metadata["point count"] = data["time"].shape[0] metadata["time"] = notes["Time"] metadata.update(meta_override) dd = {"data": data, "metadata": metadata} if callback is not None: callback(1) return [dd]
recipe_chiaro_txt = { "descr": "exported by Optics11 Chiaro Indenter", "detect": detect_txt, "loader": load_txt, "suffix": ".txt", "modalities": ["force-distance"], "maker": "Optics11 Life", }