Beispiel #1
0
def is_NXentry_with_default_NXdata(group, validate=True):
    """Return True if group is a valid NXentry defining a valid default
    NXdata.

    :param group: h5py-like object.
    :param bool validate: Set this to False if you are sure that the target group
        is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(target_group)`
        returns True). Parameter provided for optimisation purposes."""
    if not is_group(group):
        return False

    if get_attr_as_unicode(group, "NX_class") != "NXentry":
        return False

    default_nxdata_name = group.attrs.get("default")
    if default_nxdata_name is None or default_nxdata_name not in group:
        return False

    default_nxdata_group = group.get(default_nxdata_name)

    if not is_group(default_nxdata_group):
        return False

    if not validate:
        return True
    else:
        return is_valid_nxdata(default_nxdata_group)
Beispiel #2
0
def get_default(group, validate=True):
    """Return a :class:`NXdata` object corresponding to the default NXdata group
    in the group specified as parameter.

    This function can find the NXdata if the group is already a NXdata, or
    if it is a NXentry defining a default NXdata, or if it is a NXroot
    defining such a default valid NXentry.

    Return None if no valid NXdata could be found.

    :param group: h5py-like group following the Nexus specification
        (NXdata, NXentry or NXroot).
    :param bool validate: Set this to False if you are sure that group
        is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(group)`
        returns True). Parameter provided for optimisation purposes.
    :return: :class:`NXdata` object or None
    :raise TypeError: if group is not a h5py-like group
    """
    if not is_group(group):
        raise TypeError("Provided parameter is not a h5py-like group")

    if is_NXroot_with_default_NXdata(group, validate=validate):
        default_entry = group[group.attrs["default"]]
        default_data = default_entry[default_entry.attrs["default"]]
    elif is_NXentry_with_default_NXdata(group, validate=validate):
        default_data = group[group.attrs["default"]]
    elif not validate or is_valid_nxdata(group):
        default_data = group
    else:
        return None

    return NXdata(default_data, validate=False)
Beispiel #3
0
def is_NXroot_with_default_NXdata(group, validate=True):
    """Return True if group is a valid NXroot defining a default NXentry
    defining a valid default NXdata.

    :param group: h5py-like object.
    :param bool validate: Set this to False if you are sure that the target group
        is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(target_group)`
        returns True). Parameter provided for optimisation purposes.
    """
    if not is_group(group):
        return False

    # A NXroot is supposed to be at the root of a data file, and @NX_class
    # is therefore optional. We accept groups that are not located at the root
    # if they have @NX_class=NXroot (use case: several nexus files archived
    # in a single HDF5 file)
    if get_attr_as_unicode(group, "NX_class") != "NXroot" and not is_file(group):
        return False

    default_nxentry_name = group.attrs.get("default")
    if default_nxentry_name is None or default_nxentry_name not in group:
        return False

    default_nxentry_group = group.get(default_nxentry_name)
    return is_NXentry_with_default_NXdata(default_nxentry_group,
                                          validate=validate)
Beispiel #4
0
def is_NXroot_with_default_NXdata(group, validate=True):
    """Return True if group is a valid NXroot defining a default NXentry
    defining a valid default NXdata.

    .. note::

        A NXroot group cannot directly define a default NXdata. If a
        *@default* argument is present, it must point to a NXentry group.
        This NXentry must define a valid NXdata for this function to return
        True.

    :param group: h5py-like object.
    :param bool validate: Set this to False if you are sure that the target group
        is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(target_group)`
        returns True). Parameter provided for optimisation purposes.
    """
    if not is_group(group):
        return False

    # A NXroot is supposed to be at the root of a data file, and @NX_class
    # is therefore optional. We accept groups that are not located at the root
    # if they have @NX_class=NXroot (use case: several nexus files archived
    # in a single HDF5 file)
    if get_attr_as_unicode(group,
                           "NX_class") != "NXroot" and not is_file(group):
        return False

    default_nxentry_name = group.attrs.get("default")
    if default_nxentry_name is None or default_nxentry_name not in group:
        return False

    default_nxentry_group = group.get(default_nxentry_name)
    return is_NXentry_with_default_NXdata(default_nxentry_group,
                                          validate=validate)
Beispiel #5
0
def is_group_with_default_NXdata(group, validate=True):
    """Return True if group defines a valid default
    NXdata.

    .. note::

        See https://github.com/silx-kit/silx/issues/2215

    :param group: h5py-like object.
    :param bool validate: Set this to skip the NXdata validation, and only
        check the existence of the group.
        Parameter provided for optimisation purposes, to avoid double
        validation if the validation is already performed separately."""
    default_nxdata_name = group.attrs.get("default")
    if default_nxdata_name is None or default_nxdata_name not in group:
        return False

    default_nxdata_group = group.get(default_nxdata_name)

    if not is_group(default_nxdata_group):
        return False

    if not validate:
        return True
    else:
        return is_valid_nxdata(default_nxdata_group)
Beispiel #6
0
def is_group_with_default_NXdata(group, validate=True):
    """Return True if group defines a valid default
    NXdata.

    .. note::

        See https://github.com/silx-kit/silx/issues/2215

    :param group: h5py-like object.
    :param bool validate: Set this to skip the NXdata validation, and only
        check the existence of the group.
        Parameter provided for optimisation purposes, to avoid double
        validation if the validation is already performed separately."""
    default_nxdata_name = group.attrs.get("default")
    if default_nxdata_name is None or default_nxdata_name not in group:
        return False

    default_nxdata_group = group.get(default_nxdata_name)

    if not is_group(default_nxdata_group):
        return False

    if not validate:
        return True
    else:
        return is_valid_nxdata(default_nxdata_group)
Beispiel #7
0
def get_default(group, validate=True):
    """Return a :class:`NXdata` object corresponding to the default NXdata group
    in the group specified as parameter.

    This function can find the NXdata if the group is already a NXdata, or
    if it is a NXentry defining a default NXdata, or if it is a NXroot
    defining such a default valid NXentry.

    Return None if no valid NXdata could be found.

    :param group: h5py-like group following the Nexus specification
        (NXdata, NXentry or NXroot).
    :param bool validate: Set this to False if you are sure that group
        is valid NXdata (i.e. :func:`silx.io.nxdata.is_valid_nxdata(group)`
        returns True). Parameter provided for optimisation purposes.
    :return: :class:`NXdata` object or None
    :raise TypeError: if group is not a h5py-like group
    """
    if not is_group(group):
        raise TypeError("Provided parameter is not a h5py-like group")

    if is_NXroot_with_default_NXdata(group, validate=validate):
        default_entry = group[group.attrs["default"]]
        default_data = default_entry[default_entry.attrs["default"]]
    elif is_group_with_default_NXdata(group, validate=validate):
        default_data = group[group.attrs["default"]]
    elif not validate or is_valid_nxdata(group):
        default_data = group
    else:
        return None

    return NXdata(default_data, validate=False)
Beispiel #8
0
def is_NXentry_with_default_NXdata(group, validate=True):
    """Return True if group is a valid NXentry defining a valid default
    NXdata.

    :param group: h5py-like object.
    :param bool validate: Set this to skip the NXdata validation, and only
        check the existence of the group.
        Parameter provided for optimisation purposes, to avoid double
        validation if the validation is already performed separately."""
    if not is_group(group):
        return False

    if get_attr_as_unicode(group, "NX_class") != "NXentry":
        return False

    return is_group_with_default_NXdata(group, validate)
Beispiel #9
0
def is_NXentry_with_default_NXdata(group, validate=True):
    """Return True if group is a valid NXentry defining a valid default
    NXdata.

    :param group: h5py-like object.
    :param bool validate: Set this to skip the NXdata validation, and only
        check the existence of the group.
        Parameter provided for optimisation purposes, to avoid double
        validation if the validation is already performed separately."""
    if not is_group(group):
        return False

    if get_attr_as_unicode(group, "NX_class") != "NXentry":
        return False

    return is_group_with_default_NXdata(group, validate)
Beispiel #10
0
    def _validate(self):
        """Fill :attr:`issues` with error messages for each error found."""
        if not is_group(self.group):
            raise TypeError("group must be a h5py-like group")
        if get_attr_as_unicode(self.group, "NX_class") != "NXdata":
            self.issues.append("Group has no attribute @NX_class='NXdata'")

        signal_name = get_signal_name(self.group)
        if signal_name is None:
            self.issues.append("No @signal attribute on the NXdata group, "
                               "and no dataset with a @signal=1 attr found")
            # very difficult to do more consistency tests without signal
            return

        elif signal_name not in self.group or not is_dataset(self.group[signal_name]):
            self.issues.append("Cannot find signal dataset '%s'" % signal_name)
            return

        auxiliary_signals_names = get_auxiliary_signals_names(self.group)
        self.issues += validate_auxiliary_signals(self.group,
                                                  signal_name,
                                                  auxiliary_signals_names)

        if "axes" in self.group.attrs:
            axes_names = get_attr_as_unicode(self.group, "axes")
            if isinstance(axes_names, (six.text_type, six.binary_type)):
                axes_names = [axes_names]

            self.issues += validate_number_of_axes(self.group, signal_name,
                                                   num_axes=len(axes_names))

            # Test consistency of @uncertainties
            uncertainties_names = get_uncertainties_names(self.group, signal_name)
            if uncertainties_names is not None:
                if len(uncertainties_names) != len(axes_names):
                    self.issues.append("@uncertainties does not define the same " +
                                       "number of fields than @axes")

            # Test individual axes
            is_scatter = True  # true if all axes have the same size as the signal
            signal_size = 1
            for dim in self.group[signal_name].shape:
                signal_size *= dim
            polynomial_axes_names = []
            for i, axis_name in enumerate(axes_names):

                if axis_name == ".":
                    continue
                if axis_name not in self.group or not is_dataset(self.group[axis_name]):
                    self.issues.append("Could not find axis dataset '%s'" % axis_name)
                    continue

                axis_size = 1
                for dim in self.group[axis_name].shape:
                    axis_size *= dim

                if len(self.group[axis_name].shape) != 1:
                    # I don't know how to interpret n-D axes
                    self.issues.append("Axis %s is not 1D" % axis_name)
                    continue
                else:
                    # for a  1-d axis,
                    fg_idx = self.group[axis_name].attrs.get("first_good", 0)
                    lg_idx = self.group[axis_name].attrs.get("last_good", len(self.group[axis_name]) - 1)
                    axis_len = lg_idx + 1 - fg_idx

                if axis_len != signal_size:
                    if axis_len not in self.group[signal_name].shape + (1, 2):
                        self.issues.append(
                                "Axis %s number of elements does not " % axis_name +
                                "correspond to the length of any signal dimension,"
                                " it does not appear to be a constant or a linear calibration," +
                                " and this does not seem to be a scatter plot.")
                        continue
                    elif axis_len in (1, 2):
                        polynomial_axes_names.append(axis_name)
                    is_scatter = False
                else:
                    if not is_scatter:
                        self.issues.append(
                                "Axis %s number of elements is equal " % axis_name +
                                "to the length of the signal, but this does not seem" +
                                " to be a scatter (other axes have different sizes)")
                        continue

                # Test individual uncertainties
                errors_name = axis_name + "_errors"
                if errors_name not in self.group and uncertainties_names is not None:
                    errors_name = uncertainties_names[i]
                    if errors_name in self.group and axis_name not in polynomial_axes_names:
                        if self.group[errors_name].shape != self.group[axis_name].shape:
                            self.issues.append(
                                    "Errors '%s' does not have the same " % errors_name +
                                    "dimensions as axis '%s'." % axis_name)

        # test dimensions of errors associated with signal
        if "errors" in self.group and is_dataset(self.group["errors"]):
            if self.group["errors"].shape != self.group[signal_name].shape:
                self.issues.append(
                        "Dataset containing standard deviations must " +
                        "have the same dimensions as the signal.")
Beispiel #11
0
    def _validate(self):
        """Fill :attr:`issues` with error messages for each error found."""
        if not is_group(self.group):
            raise TypeError("group must be a h5py-like group")
        if get_attr_as_unicode(self.group, "NX_class") != "NXdata":
            self.issues.append("Group has no attribute @NX_class='NXdata'")
            return

        signal_name = get_signal_name(self.group)
        if signal_name is None:
            self.issues.append("No @signal attribute on the NXdata group, "
                               "and no dataset with a @signal=1 attr found")
            # very difficult to do more consistency tests without signal
            return

        elif signal_name not in self.group or not is_dataset(
                self.group[signal_name]):
            self.issues.append("Cannot find signal dataset '%s'" % signal_name)
            return

        auxiliary_signals_names = get_auxiliary_signals_names(self.group)
        self.issues += validate_auxiliary_signals(self.group, signal_name,
                                                  auxiliary_signals_names)

        if "axes" in self.group.attrs:
            axes_names = get_attr_as_unicode(self.group, "axes")
            if isinstance(axes_names, (six.text_type, six.binary_type)):
                axes_names = [axes_names]

            self.issues += validate_number_of_axes(self.group,
                                                   signal_name,
                                                   num_axes=len(axes_names))

            # Test consistency of @uncertainties
            uncertainties_names = get_uncertainties_names(
                self.group, signal_name)
            if uncertainties_names is not None:
                if len(uncertainties_names) != len(axes_names):
                    if len(uncertainties_names) < len(axes_names):
                        # ignore the field to avoid index error in the axes loop
                        uncertainties_names = None
                        self.issues.append(
                            "@uncertainties does not define the same " +
                            "number of fields than @axes. Field ignored")
                    else:
                        self.issues.append(
                            "@uncertainties does not define the same " +
                            "number of fields than @axes")

            # Test individual axes
            is_scatter = True  # true if all axes have the same size as the signal
            signal_size = 1
            for dim in self.group[signal_name].shape:
                signal_size *= dim
            polynomial_axes_names = []
            for i, axis_name in enumerate(axes_names):

                if axis_name == ".":
                    continue
                if axis_name not in self.group or not is_dataset(
                        self.group[axis_name]):
                    self.issues.append("Could not find axis dataset '%s'" %
                                       axis_name)
                    continue

                axis_size = 1
                for dim in self.group[axis_name].shape:
                    axis_size *= dim

                if len(self.group[axis_name].shape) != 1:
                    # I don't know how to interpret n-D axes
                    self.issues.append("Axis %s is not 1D" % axis_name)
                    continue
                else:
                    # for a  1-d axis,
                    fg_idx = self.group[axis_name].attrs.get("first_good", 0)
                    lg_idx = self.group[axis_name].attrs.get(
                        "last_good",
                        len(self.group[axis_name]) - 1)
                    axis_len = lg_idx + 1 - fg_idx

                if axis_len != signal_size:
                    if axis_len not in self.group[signal_name].shape + (1, 2):
                        self.issues.append(
                            "Axis %s number of elements does not " %
                            axis_name +
                            "correspond to the length of any signal dimension,"
                            " it does not appear to be a constant or a linear calibration,"
                            + " and this does not seem to be a scatter plot.")
                        continue
                    elif axis_len in (1, 2):
                        polynomial_axes_names.append(axis_name)
                    is_scatter = False
                else:
                    if not is_scatter:
                        self.issues.append(
                            "Axis %s number of elements is equal " %
                            axis_name +
                            "to the length of the signal, but this does not seem"
                            +
                            " to be a scatter (other axes have different sizes)"
                        )
                        continue

                # Test individual uncertainties
                errors_name = axis_name + "_errors"
                if errors_name not in self.group and uncertainties_names is not None:
                    errors_name = uncertainties_names[i]
                    if errors_name in self.group and axis_name not in polynomial_axes_names:
                        if self.group[errors_name].shape != self.group[
                                axis_name].shape:
                            self.issues.append(
                                "Errors '%s' does not have the same " %
                                errors_name +
                                "dimensions as axis '%s'." % axis_name)

        # test dimensions of errors associated with signal

        signal_errors = signal_name + "_errors"
        if "errors" in self.group and is_dataset(self.group["errors"]):
            errors = "errors"
        elif signal_errors in self.group and is_dataset(
                self.group[signal_errors]):
            errors = signal_errors
        else:
            errors = None
        if errors:
            if self.group[errors].shape != self.group[signal_name].shape:
                # In principle just the same size should be enough but
                # NeXus documentation imposes to have the same shape
                self.issues.append(
                    "Dataset containing standard deviations must " +
                    "have the same dimensions as the signal.")
Beispiel #12
0
def is_valid_nxdata(group):  # noqa
    """Check if a h5py group is a **valid** NX_data group.

    If the group does not have attribute *@NX_class=NXdata*, this function
    simply returns *False*.

    Else, warning messages are logged to troubleshoot malformed NXdata groups
    prior to returning *False*.

    :param group: h5py-like group
    :return: True if this NXdata group is valid.
    :raise TypeError: if group is not a h5py group, a spech5 group,
        or a fabioh5 group
    """
    if not is_group(group):
        raise TypeError("group must be a h5py-like group")
    if get_attr_as_unicode(group, "NX_class") != "NXdata":
        return False

    signal_name = _get_signal_name(group)
    if signal_name is None:
        _nxdata_warning(
            "No @signal attribute on the NXdata group, "
            "and no dataset with a @signal=1 attr found", group.name)
        return False

    if signal_name not in group or not is_dataset(group[signal_name]):
        _nxdata_warning("Cannot find signal dataset '%s'" % signal_name,
                        group.name)
        return False

    auxiliary_signals_names = _get_auxiliary_signals_names(group)
    if not _are_auxiliary_signals_valid(group, signal_name,
                                        auxiliary_signals_names):
        return False

    if "axes" in group.attrs:
        axes_names = get_attr_as_unicode(group, "axes")
        if isinstance(axes_names, (six.text_type, six.binary_type)):
            axes_names = [axes_names]

        if not _has_valid_number_of_axes(
                group, signal_name, num_axes=len(axes_names)):
            return False

        # Test consistency of @uncertainties
        uncertainties_names = _get_uncertainties_names(group, signal_name)
        if uncertainties_names is not None:
            if len(uncertainties_names) != len(axes_names):
                _nxdata_warning(
                    "@uncertainties does not define the same " +
                    "number of fields than @axes", group.name)
                return False

        # Test individual axes
        is_scatter = True  # true if all axes have the same size as the signal
        signal_size = 1
        for dim in group[signal_name].shape:
            signal_size *= dim
        polynomial_axes_names = []
        for i, axis_name in enumerate(axes_names):

            if axis_name == ".":
                continue
            if axis_name not in group or not is_dataset(group[axis_name]):
                _nxdata_warning("Could not find axis dataset '%s'" % axis_name,
                                group.name)
                return False

            axis_size = 1
            for dim in group[axis_name].shape:
                axis_size *= dim

            if len(group[axis_name].shape) != 1:
                # too me, it makes only sense to have a n-D axis if it's total
                # size is exactly the signal's size (weird n-d scatter)
                if axis_size != signal_size:
                    _nxdata_warning(
                        "Axis %s is not a 1D dataset" % axis_name +
                        " and its shape does not match the signal's shape",
                        group.name)
                    return False
                axis_len = axis_size
            else:
                # for a  1-d axis,
                fg_idx = group[axis_name].attrs.get("first_good", 0)
                lg_idx = group[axis_name].attrs.get("last_good",
                                                    len(group[axis_name]) - 1)
                axis_len = lg_idx + 1 - fg_idx

            if axis_len != signal_size:
                if axis_len not in group[signal_name].shape + (1, 2):
                    _nxdata_warning(
                        "Axis %s number of elements does not " % axis_name +
                        "correspond to the length of any signal dimension,"
                        " it does not appear to be a constant or a linear calibration,"
                        + " and this does not seem to be a scatter plot.",
                        group.name)
                    return False
                elif axis_len in (1, 2):
                    polynomial_axes_names.append(axis_name)
                is_scatter = False
            else:
                if not is_scatter:
                    _nxdata_warning(
                        "Axis %s number of elements is equal " % axis_name +
                        "to the length of the signal, but this does not seem" +
                        " to be a scatter (other axes have different sizes)",
                        group.name)
                    return False

            # Test individual uncertainties
            errors_name = axis_name + "_errors"
            if errors_name not in group and uncertainties_names is not None:
                errors_name = uncertainties_names[i]
                if errors_name in group and axis_name not in polynomial_axes_names:
                    if group[errors_name].shape != group[axis_name].shape:
                        _nxdata_warning(
                            "Errors '%s' does not have the same " % errors_name
                            + "dimensions as axis '%s'." % axis_name,
                            group.name)
                        return False

    # test dimensions of errors associated with signal
    if "errors" in group and is_dataset(group["errors"]):
        if group["errors"].shape != group[signal_name].shape:
            _nxdata_warning(
                "Dataset containing standard deviations must " +
                "have the same dimensions as the signal.", group.name)
            return False
    return True