Source code for SciFiReaders.readers.microscopy.spm.stm.nanonis_3ds

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 5 16:43:00 2021

@author: Rama Vasudevan
"""
import numpy as np  # For array operations
import sidpy as sid
from sidpy.sid import Reader, Dimension, DimensionType

try:
    import nanonispy as nap
except ModuleNotFoundError:
    nap = None


[docs]class Nanonis3dsReader(Reader): @staticmethod def _parse_3ds_parms(header_dict, signal_dict): """ Parse 3ds files. Parameters ---------- header_dict : dict signal_dict : dict Returns ------- parm_dict : dict """ parm_dict = dict() data_dict = dict() # Create dictionary with measurement parameters meas_parms = {key: value for key, value in header_dict.items() if value is not None} channels = meas_parms.pop('channels') for key, parm_grid in zip(meas_parms.pop('fixed_parameters') + meas_parms.pop('experimental_parameters'), signal_dict['params'].T): # Collapse the parm_grid along one axis if it's constant # along said axis if parm_grid.ndim > 1: dim_slice = list() # Find dimensions that are constant for idim in range(parm_grid.ndim): tmp_grid = np.moveaxis(parm_grid.copy(), idim, 0) if np.all(np.equal(tmp_grid[0], tmp_grid[1])): dim_slice.append(0) else: dim_slice.append(slice(None)) # print(key, dim_slice) # print(parm_grid[tuple(dim_slice)]) parm_grid = parm_grid[tuple(dim_slice)] meas_parms[key] = parm_grid parm_dict['meas_parms'] = meas_parms # Create dictionary with channel parameters and # save channel data before renaming keys data_channel_parms = dict() for chan_name in channels: splitted_chan_name = chan_name.split(maxsplit=2) if len(splitted_chan_name) == 2: direction = 'forward' elif len(splitted_chan_name) == 3: direction = 'backward' splitted_chan_name.pop(1) name, unit = splitted_chan_name key = ' '.join((name, direction)) data_channel_parms[key] = {'Name': name, 'Direction': direction, 'Unit': unit.strip('()'), } data_dict[key] = signal_dict.pop(chan_name) parm_dict['channel_parms'] = data_channel_parms # Add remaining signal_dict elements to data_dict data_dict.update(signal_dict) # Position dimensions nx, ny = header_dict['dim_px'] if 'X (m)' in parm_dict: row_vals = parm_dict.pop('X (m)') else: row_vals = np.arange(nx, dtype=np.float32) if 'Y (m)' in parm_dict: col_vals = parm_dict.pop('Y (m)') else: col_vals = np.arange(ny, dtype=np.float32) pos_vals = np.hstack([row_vals.reshape(-1, 1), col_vals.reshape(-1, 1)]) pos_names = ['X', 'Y'] dims = [Dimension(values, name=label, quantity='Length', units='nm', dimension_type=DimensionType.SPATIAL) for label, values in zip(pos_names, pos_vals.T)] # Spectroscopic dimensions sweep_signal = header_dict['sweep_signal'] spec_label, spec_unit = sweep_signal.split(maxsplit=1) spec_unit = spec_unit.strip('()') # parm_dict['sweep_signal'] = (sweep_name, sweep_unit) dc_offset = data_dict['sweep_signal'] spec_dim = Dimension(dc_offset, quantity='Bias', name=spec_label, units=spec_unit, dimension_type=DimensionType.SPECTRAL) dims.append(spec_dim) data_dict['Dimensions'] = dims return parm_dict, data_dict
[docs] def read(self): """ Returns ------- list of sidpy.Dataset objects containing the spectroscopy data """ reader = nap.read.Grid nanonis_data = reader(self._input_file_path) header_dict = nanonis_data.header signal_dict = nanonis_data.signals parm_dict, data_dict = self._parse_3ds_parms(header_dict, signal_dict) self.parm_dict = parm_dict self.data_dict = data_dict #Specify dimensions x_dim = self.data_dict['Dimensions'][0] y_dim = self.data_dict['Dimensions'][1] z_dim = self.data_dict['Dimensions'][2] dataset_list = [] channel_parms = self.parm_dict['channel_parms'] orig_metadata = self.parm_dict['meas_parms'] chan_names = list(self.parm_dict['channel_parms'].keys()) for dataset_name in chan_names: data_mat = self.data_dict[dataset_name] #Make a sidpy dataset data_set = sid.Dataset.from_array(data_mat, name = dataset_name) #Set the data type data_set.data_type = sid.DataType.SPECTRAL_IMAGE metadata = channel_parms[dataset_name] # Add quantity and units data_set.units = metadata['Unit'] data_set.quantity = metadata['Name'] # Add dimension info data_set.set_dimension(0, x_dim) data_set.set_dimension(1, y_dim) data_set.set_dimension(2, z_dim) # append metadata def merge_dict(dict1, dict2): res = {**dict1, **dict2} return res chan_metadata = self.parm_dict['channel_parms'][dataset_name] data_set.metadata = merge_dict(chan_metadata,orig_metadata) dataset_list.append(data_set) return dataset_list return
[docs] def can_read(self): """ Tests whether or not the provided file has a .3ds extension Returns ------- """ if nap is None: return False return super(Nanonis3dsReader, self).can_read(extension='3ds')