예제 #1
0
    def __getter__(self, name, instantiate=True):
        """Loads the specified name from a file on disk.

        Parameters:
            name (key type): The canonical mapping key to get the dataObject. By default
                the baseFolder class uses a :py:class:`regexpDict` to store objects in.

        Keyword Arguments:
            instatiate (bool): IF True (default) then always return a :py:class:`Stoner.Core.Data` object. If False,
                the __getter__ method may return a key that can be used by it later to actually get the
                :py:class:`Stoner.Core.Data` object.

        Returns:
            (metadataObject): The metadataObject
        """
        assertion(name is not None, "Cannot get an anonympus entry!")
        try:  # Try the parent methods first
            return super(DiskBasedFolder,
                         self).__getter__(name, instantiate=instantiate)
        except (AttributeError, IndexError, KeyError):
            pass
        # Find a filename and load
        fname = name if path.exists(name) else path.join(self.directory, name)
        try:
            tmp = self.type(self.loader(fname, **self.extra_args))
        except StonerUnrecognisedFormat:
            return None
        if not isinstance(getattr(tmp, "filename", None), string_types):
            tmp.filename = path.basename(fname)
        # Process file hooks
        tmp = self.on_load_process(tmp)
        tmp = self._update_from_object_attrs(tmp)
        # Store the result
        self.__setter__(name, tmp)
        return tmp
예제 #2
0
    def __parse_VSM(self, header_line=3, data_line=3, header_delim=","):
        """An intrernal function for parsing deliminated data without a leading column of metadata.copy

        Keyword Arguments:
            header_line (int): The line in the file that contains the column headers.
                If None, then column headers are auotmatically generated.
            data_line (int): The line on which the data starts
            header_delim (strong): The delimiter used for separating header values

        Returns:
            Nothing, but modifies the current object.

        Note:
            The default values are configured fir read VSM data files
        """
        try:
            with io.open(self.filename, errors="ignore",
                         encoding="utf-8") as f:
                for i, line in enumerate(f):
                    if i == 0:
                        self["Timestamp"] = line.strip()
                        check = datetime.strptime(self["Timestamp"],
                                                  "%a %b %d %H:%M:%S %Y")
                        if check is None:
                            raise Core.StonerLoadError("Not a VSM file ?")
                    elif i == 1:
                        assertion(line.strip() == "")
                    elif i == 2:
                        header_string = line.strip()
                    elif i == header_line:
                        unit_string = line.strip()
                        column_headers = [
                            "{} ({})".format(h.strip(), u.strip())
                            for h, u in zip(header_string.split(header_delim),
                                            unit_string.split(header_delim))
                        ]
                    elif i > 3:
                        break
        except (StonerAssertionError, ValueError, AssertionError,
                TypeError) as e:
            raise Core.StonerLoadError("Not a VSM File" + str(e.args))
        self.data = np.genfromtxt(
            self.filename,
            dtype="float",
            usemask=True,
            skip_header=data_line - 1,
            missing_values=["6:0", "---"],
            invalid_raise=False,
        )

        self.data = np.ma.mask_rows(self.data)
        cols = self.data.shape[1]
        self.data = np.reshape(self.data.compressed(), (-1, cols))
        self.column_headers = column_headers
        self.setas(x="H_vsm (T)", y="m (emu)")  # pylint: disable=not-callable
예제 #3
0
    def crop_text(self, copy=False):
        """Crop the bottom text area from a standard Kermit image stack

        Returns:
            (self):
            cropped image
        """
        assertion(
            (self[0].shape == AN_IM_SIZE or self[0].shape == IM_SIZE), "Need a full sized Kerr image to crop"
        )  # check it's a normal image
        crop = (0, IM_SIZE[1], 0, IM_SIZE[0])
        self.crop_stack(box=crop)
예제 #4
0
 def _get_scalebar(self):
     """Get the length in pixels of the image scale bar"""
     box = (0, 419, 519, 520)  # row where scalebar exists
     im = self.crop(box=box, copy=True)
     im = im.astype(float)
     im = (im - im.min()) / (im.max() - im.min())
     im = exposure.rescale_intensity(im, in_range=(0.49, 0.5))  # saturate black and white pixels
     im = exposure.rescale_intensity(im)  # make sure they're black and white
     im = np.diff(im[0])  # 1d numpy array, differences
     lim = [np.where(im > 0.9)[0][0], np.where(im < -0.9)[0][0]]  # first occurance of both cases
     assertion(len(lim) == 2, "Couldn't find scalebar")
     return lim[1] - lim[0]
예제 #5
0
 def _read_uvwdata(self, filename, fmt, lineno):
     """Read the numerical data taking account of the format."""
     if fmt == "Text":
         uvwdata = np.genfromtxt(self.filename, skip_header=lineno + 2)
     elif fmt == "Binary 4":
         if self["version"] == 1:
             dt = np.dtype(">f4")
         else:
             dt = np.dtype("<f4")
         with io.open(filename, "rb") as bindata:
             bindata.seek(self._ptr)
             uvwdata = np.fromfile(bindata,
                                   dtype=dt,
                                   count=1 +
                                   self["xnodes"] * self["ynodes"] *
                                   self["znodes"] * self["valuedim"])
             assertion(
                 uvwdata[0] == 1234567.0,
                 "Binary 4 format check value incorrect ! Actual Value was {}"
                 .format(uvwdata[0]),
             )
         uvwdata = uvwdata[1:]
         uvwdata = np.reshape(uvwdata, (-1, self["valuedim"]))
     elif fmt == "Binary 8":
         if self["version"] == 1:
             dt = np.dtype(">f8")
         else:
             dt = np.dtype("<f8")
         with io.open(filename, "rb") as bindata:
             bindata.seek(self._ptr)
             uvwdata = np.fromfile(bindata,
                                   dtype=dt,
                                   count=1 +
                                   self["xnodes"] * self["ynodes"] *
                                   self["znodes"] * self["valuedim"])
             assertion(
                 (uvwdata[0] == 123456789012345.0),
                 "Binary 4 format check value incorrect ! Actual Value was {}"
                 .format(uvwdata[0]),
             )
         uvwdata = np.reshape(uvwdata, (-1, self["valuedim"]))
     else:
         raise StonerLoadError("Unknow OVF Format {}".format(fmt))
     return uvwdata
예제 #6
0
    def peaks(self, **kargs):
        """Locates peaks and/or troughs in a column of data by using SG-differentiation.

        Args:
            ycol (index):
                the column name or index of the data in which to search for peaks
            width (int or float):
                the expected minium halalf-width of a peak in terms of the number of data points (int) or distance
                in x (float). This is used in the differnetiation code to find local maxima. Bigger equals less
                sensitive to experimental noise, smaller means better eable to see sharp peaks
            poly (int):
                the order of polynomial to use when differentiating the data to locate a peak. Must >=2, higher numbers
                will find sharper peaks more accurately but at the risk of finding more false positives.

        Keyword Arguments:
            significance (float):
                used to decide whether a local maxmima is a significant peak. Essentially just the curvature
                of the data. Bigger means less sensistive, smaller means more likely to detect noise. Default is the
                maximum curvature/(2*width)
            xcol (index or None):
                name or index of data column that p[rovides the x-coordinate (default None)
            peaks (bool):
                select whether to measure peaks in data (default True)
            troughs (bool):
                select whether to measure troughs in data (default False)
            sort (bool):
                Sor the results by significance of peak
            modify (book):
                If true, then the returned object is a copy of self with only the peaks/troughs left in the data.
            full_data (bool):
                If True (default) then all columns of the data at which peaks in the *ycol* column are found.
                *modify* true implies *full_data* is also true. If *full_data* is False, then only the x-column
                values of the peaks are returned.

        Returns:
            (various):
                If *modify* is true, then returns a the AnalysisMixin with the data set to just the peaks/troughs.
                If *modify* is false (default), then the return value depends on *ycol* and *xcol*. If *ycol* is
                not None and *xcol* is None, then returns conplete rows of data corresponding to the found
                peaks/troughs. If *xcol* is not None, or *ycol* is None and *xcol* is None, then returns a 1D array
                of the x positions of the peaks/troughs.

        See Also:
            User guide section :ref:`peak_finding`
        """
        width = kargs.pop("width", int(len(self) / 20))
        peaks = kargs.pop("peaks", True)
        troughs = kargs.pop("troughs", False)
        poly = kargs.pop("poly", 2)
        assertion(
            poly >= 2,
            "poly must be at least 2nd order in peaks for checking for significance of peak or trough"
        )

        sort = kargs.pop("sort", False)
        modify = kargs.pop("modify", False)
        full_data = kargs.pop("full_data", True)
        _ = self._col_args(scalar=False,
                           xcol=kargs.pop("xcol", None),
                           ycol=kargs.pop("ycol", None))
        xcol, ycol = _.xcol, _.ycol
        if isIterable(ycol):
            ycol = ycol[0]
        if isinstance(
                width,
                float):  # Convert a floating point width unto an integer.
            xmin, xmax = self.span(xcol)
            width = int(len(self) * width / (xmax - xmin))
        width = max(width, poly + 1)
        setas = self.setas.clone  # pylint: disable=E0203
        self.setas = ""
        d1 = self.SG_Filter(ycol, xcol=xcol, points=width, poly=poly,
                            order=1).ravel()
        d2 = self.SG_Filter(
            ycol, xcol=xcol, points=2 * width, poly=poly,
            order=2).ravel()  # 2nd differential requires more smoothing

        # We're going to ignore the start and end of the arrays
        index_offset = int(width / 2)
        d1 = d1[index_offset:-index_offset]
        d2 = d2[index_offset:-index_offset]

        # Pad the ends of d2 with the mean value
        pad = np.mean(d2[index_offset:-index_offset])
        d2[:index_offset] = pad
        d2[-index_offset:] = pad

        # Set the significance from the 2nd ifferential if not already set
        significance = kargs.pop(
            "significance",
            np.max(np.abs(d2)) /
            (2 * width))  # Base an apriori significance on max d2y/dx2 / 20
        if isinstance(significance,
                      int):  # integer significance is inverse to floating
            significance = np.max(
                np.abs(d2)
            ) / significance  # Base an apriori significance on max d2y/dx2 / 20

        d2_interp = interp1d(np.arange(len(d2)), d2, kind="cubic")
        # Ensure we have some X-data
        if xcol is None:
            xdata = np.arange(len(self))
        else:
            xdata = self.column(xcol)
        xdata = interp1d(np.arange(len(self)), xdata, kind="cubic")

        possible_peaks = np.array(
            threshold(0, d1, rising=troughs, falling=peaks))
        curvature = np.abs(d2_interp(possible_peaks))

        # Filter just the significant peaks
        possible_peaks = np.array([
            p for ix, p in enumerate(possible_peaks)
            if abs(curvature[ix]) > significance
        ])
        # Sort in order of significance
        if sort:
            possible_peaks = np.take(
                possible_peaks, np.argsort(np.abs(d2_interp(possible_peaks))))

        xdat = xdata(possible_peaks + index_offset)

        if modify:
            self.data = self.interpolate(xdat, xcol=xcol, kind="cubic")
            ret = self
        elif full_data:
            ret = self.interpolate(xdat, kind="cubic", xcol=False)
        else:
            ret = xdat
        self.setas = setas
        # Return - but remembering to add back on the offset that we took off due to differentials not working at
        # start and end
        return ret
예제 #7
0
    def _load(self, filename=None, *args, **kargs):
        """Load function. File format has space delimited columns from row 3 onwards."""
        if filename is None or not filename:
            self.get_filename("r")
        else:
            self.filename = filename

        self._ptr = 0
        with io.open(self.filename, "r", errors="ignore",
                     encoding="utf-8") as data:  # Slightly ugly text handling
            line = next(data)
            self._ptr += len(line)
            line = line.strip()
            if "OOMMF: rectangular mesh" in line:
                if "v1.0" in line:
                    self["version"] = 1
                elif "v2.0" in line:
                    self["version"] = 2
                else:
                    raise StonerLoadError(
                        "Cannot determine version of OOMMFF file")
            else:  # bug out oif we don't like the header
                raise StonerLoadError(
                    "Not n OOMMF OVF File: opening line eas {}".format(line))
            pattern = re.compile(r"#\s*([^\:]+)\:\s+(.*)$")
            i = None
            for i, line in enumerate(data):
                self._ptr += len(line)
                line.strip()
                if line.startswith(
                        "# Begin: Data"):  # marks the start of the trext
                    break
                elif line.startswith("# Begin:") or line.startswith("# End:"):
                    continue
                else:
                    res = pattern.match(line)
                    if res is not None:
                        key = res.group(1)
                        val = res.group(2)
                        self[key] = string_to_type(val)
                    else:
                        raise StonerLoadError("Failed to understand metadata")
            fmt = re.match(r".*Data\s+(.*)", line).group(1).strip()
            assertion(
                (self["meshtype"] == "rectangular"),
                "Sorry only OVF files with rectnagular meshes are currently supported.",
            )
            if self["version"] == 1:
                if self["meshtype"] == "rectangular":
                    self["valuedim"] = 3
                else:
                    self["valuedim"] = 6
            uvwdata = self._read_uvwdata(filename, fmt, i)

        x = (np.linspace(self["xmin"], self["xmax"], self["xnode"] + 1)[:-1] +
             self["xbase"]) * 1e9
        y = (np.linspace(self["ymin"], self["ymax"], self["ynode"] + 1)[:-1] +
             self["ybase"]) * 1e9
        z = (np.linspace(self["zmin"], self["zmax"], self["znode"] + 1)[:-1] +
             self["zbase"]) * 1e9
        (y, z, x) = (np.ravel(i) for i in np.meshgrid(y, z, x))
        self.data = np.column_stack((x, y, z, uvwdata))
        column_headers = ["X (nm)", "Y (nm)", "Z (nm)", "U", "V", "W"]
        self.setas = "xyzuvw"
        self.column_headers = column_headers
        return self