Source code for SDF.force_sdf.force_sdf

from typing import Set

from SDF.data_model import ArrayData1D, ArrayDataset1D, Parameter, Workspace

# all other parameters are taken as string
REQUIRED_PARAMETERS: Set[str] = {
    "spring_constant",
    "sensitivity",
}
REQUIRED_SEGMENT_DATA_CHANNELS: Set[str] = {
    "height",
    "vDeflection",
    "measuredHeight",
}
REQUIRED_SEGMENT_PARAMETERS: Set[str] = {
    "duration",
    "direction",
    "velocity",
}


[docs]class ForceSDF(Workspace): """ Represents a force distance curve. This class wraps a Workspace and ensures it fulfills all ForceSDF requirements. """ def __init__(self, workspace: Workspace): if not isinstance(workspace, Workspace): raise TypeError(f"Expected a Workspace, got {type(workspace)}") # check name if not workspace.name.startswith("ForceSDF"): raise ValueError(f"Expected a ForceSDF workspace, got '{workspace.name}'") super().__init__(name=workspace.name, owner=workspace.owner, date=workspace.date, comment=workspace.comment, samples=workspace.samples, parameters=workspace.parameters, instruments=workspace.instruments, datasets=workspace.datasets, workspaces=workspace.workspaces) # check for required parameters for par_name in REQUIRED_PARAMETERS: if par_name not in self.instruments["parameters"].keys(): raise ValueError(f"Required parameter '{par_name}' is missing") # check segments for workspace in self.workspaces: if not workspace.name.startswith("segment"): # skip non-segment workspaces continue self.workspaces.add(ForceSDFSegment(workspace, parent=self)) # replace workspace with segment
[docs]class ForceSDFSegment(Workspace): parent: ForceSDF def __init__(self, workspace: Workspace, parent: ForceSDF): # check name prefix = "segment " name = workspace.name if not (name.startswith(prefix) and name[len(prefix):].isdigit()): raise ValueError(f"Expected a ForceSDF segment, got '{name}'") super().__init__(name=workspace.name, owner=workspace.owner, date=workspace.date, comment=workspace.comment, samples=workspace.samples, parameters=workspace.parameters, instruments=workspace.instruments, datasets=workspace.datasets, workspaces=workspace.workspaces) self.parent = parent # generate missing data channels self.__generate_missing_data_channels_and_parameters() # check for required parameters for par_name in REQUIRED_SEGMENT_PARAMETERS: if par_name not in self.instruments["segment-parameters"].keys(): raise ValueError(f"Required parameter '{par_name}' is missing") # check for required data channels for channel_name in REQUIRED_SEGMENT_DATA_CHANNELS: if channel_name not in self.datasets.keys(): raise ValueError(f"Required data channel '{channel_name}' is missing") def __generate_missing_data_channels_and_parameters(self): measured_height = self.datasets["measuredHeight"] height = self.datasets["height"] v_deflection = self.datasets["vDeflection"] duration = self.instruments["segment-parameters"]["duration"] spring_constant = self.parent.instruments["parameters"]["spring_constant"] if not isinstance(measured_height, ArrayDataset1D): raise TypeError("measuredHeight is no ArrayDataset") if not isinstance(height, ArrayDataset1D): raise TypeError("height is no ArrayDataset") if not isinstance(v_deflection, ArrayDataset1D): raise TypeError("vDeflection is no ArrayDataset") # distance in meters if v_deflection.unit == "N" and spring_constant.unit == "N/m": distance = v_deflection.data / float(spring_constant.value) distance_unit = "m" elif v_deflection.unit == "m": distance = v_deflection.data distance_unit = "m" else: distance = v_deflection.data distance_unit = None # optional datasets if measured_height.unit == "m" and distance_unit == "m": measured_tip_sample_separation = ArrayData1D(measured_height.data + distance.data, try_hex_transformation=True) self.datasets.add(ArrayDataset1D("measuredTipSample", data=measured_tip_sample_separation, unit="m")) if height.unit == "m" and distance_unit == "m": tip_sample_separation = ArrayData1D(height.data + distance.data, try_hex_transformation=True) self.datasets.add(ArrayDataset1D("tipSample", data=tip_sample_separation, unit="m")) if measured_height.unit == "m" and duration.unit == "s": velocity = (measured_height.data[0] - measured_height.data[-1]) / float(duration.value) self.instruments["segment-parameters"].add(Parameter("velocity", velocity, "m/s"))