diff --git a/.github/workflows/plugin_test.yaml b/.github/workflows/plugin_test.yaml index 5b7b43b7d..32fd9bbfd 100644 --- a/.github/workflows/plugin_test.yaml +++ b/.github/workflows/plugin_test.yaml @@ -30,10 +30,10 @@ jobs: branch: main tests_to_run: tests/. - plugin: pynxtools-igor - branch: update-definitions + branch: main tests_to_run: tests/. - plugin: pynxtools-mpes - branch: update-definitions + branch: main tests_to_run: tests/. - plugin: pynxtools-raman branch: main diff --git a/CITATION.cff b/CITATION.cff index 67d45ad01..cf46d9c00 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,7 +4,7 @@ message: If you use this software, please cite it using the metadata from this file. type: software -version: 0.9.3 +version: 0.10.0 authors: - given-names: Sherjeel family-names: Shabih diff --git a/pyproject.toml b/pyproject.toml index 833ae9eef..cc59d830e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ dependencies = [ "lxml>=4.9.1", "toposort>=1.10.0", "anytree", + "pint==0.17", ] [project.urls] diff --git a/src/pynxtools/data/NXtest.nxdl.xml b/src/pynxtools/data/NXtest.nxdl.xml index fa2917205..329f42ef2 100644 --- a/src/pynxtools/data/NXtest.nxdl.xml +++ b/src/pynxtools/data/NXtest.nxdl.xml @@ -19,7 +19,7 @@ - + A dummy entry to test optional parent check for a required child. @@ -27,8 +27,29 @@ A dummy entry to test optional parent check for an optional child. - - + + A group with a (specified) name, but nameType not given explicitly. + + + + + + + A group with a name and nameType="specified". + + + + + + + A group with a name and nameType="any". + + + + + + + A dummy entry for a float value. @@ -52,10 +73,10 @@ - - - - + + + + @@ -64,6 +85,12 @@ + + + + + + @@ -94,5 +121,9 @@ A required NXuser entry. + + + + diff --git a/src/pynxtools/dataconverter/helpers.py b/src/pynxtools/dataconverter/helpers.py index 290b4a634..ee3647414 100644 --- a/src/pynxtools/dataconverter/helpers.py +++ b/src/pynxtools/dataconverter/helpers.py @@ -48,24 +48,25 @@ class ValidationProblem(Enum): UnitWithoutDocumentation = 1 InvalidEnum = 2 - MissingRequiredGroup = 3 - MissingRequiredField = 4 - MissingRequiredAttribute = 5 - InvalidType = 6 - InvalidDatetime = 7 - IsNotPosInt = 8 - ExpectedGroup = 9 - MissingDocumentation = 10 - MissingUnit = 11 - ChoiceValidationError = 12 - UnitWithoutField = 13 - AttributeForNonExistingField = 14 - BrokenLink = 15 - FailedNamefitting = 16 - NXdataMissingSignalData = 17 - NXdataMissingAxisData = 18 - NXdataAxisMismatch = 19 - KeyToBeRemoved = 20 + OpenEnumWithNewItem = 3 + MissingRequiredGroup = 4 + MissingRequiredField = 5 + MissingRequiredAttribute = 6 + InvalidType = 7 + InvalidDatetime = 8 + IsNotPosInt = 9 + ExpectedGroup = 10 + MissingDocumentation = 11 + MissingUnit = 12 + ChoiceValidationError = 13 + UnitWithoutField = 14 + AttributeForNonExistingField = 15 + BrokenLink = 16 + FailedNamefitting = 17 + NXdataMissingSignalData = 18 + NXdataMissingAxisData = 19 + NXdataAxisMismatch = 20 + KeyToBeRemoved = 21 class Collector: @@ -85,7 +86,11 @@ def _log(self, path: str, log_type: ValidationProblem, value: Optional[Any], *ar ) elif log_type == ValidationProblem.InvalidEnum: logger.warning( - f"The value at {path} should be one of the following: {value}" + f"The value at {path} should be one of the following: {value}." + ) + elif log_type == ValidationProblem.OpenEnumWithNewItem: + logger.info( + f"The value at {path} does not match with the enumerated items from the open enumeration: {value}." ) elif log_type == ValidationProblem.MissingRequiredGroup: logger.warning(f"The required group, {path}, hasn't been supplied.") @@ -164,7 +169,10 @@ def collect_and_log( if self.logging and path + str(log_type) + str(value) not in self.data: self._log(path, log_type, value, *args, **kwargs) # info messages should not fail validation - if log_type not in (ValidationProblem.UnitWithoutDocumentation,): + if log_type not in ( + ValidationProblem.UnitWithoutDocumentation, + ValidationProblem.OpenEnumWithNewItem, + ): self.data.add(path + str(log_type) + str(value)) def has_validation_problems(self): @@ -266,7 +274,7 @@ def get_all_parents_for(xml_elem: ET._Element) -> List[ET._Element]: root = get_appdef_root(xml_elem) inheritance_chain = [] extends = root.get("extends") - while extends is not None and extends != "NXobject": + while extends is not None: parent_xml_root, _ = get_nxdl_root_and_path(extends) extends = parent_xml_root.get("extends") inheritance_chain.append(parent_xml_root) @@ -489,6 +497,15 @@ def contains_uppercase(field_name: Optional[str]) -> bool: return any(char.isupper() for char in field_name) +def is_variadic(name: str, name_type: str) -> bool: + """ + Determine if a name is variadic based on its nameType. + """ + if name: + return False if name_type == "specified" else True + return True + + def convert_nexus_to_suggested_name(nexus_name): """Helper function to suggest a name for a group from its NeXus class.""" if contains_uppercase(nexus_name): @@ -646,7 +663,9 @@ def convert_str_to_bool_safe(value: str) -> Optional[bool]: raise ValueError(f"Could not interpret string '{value}' as boolean.") -def is_valid_data_field(value: Any, nxdl_type: str, nxdl_enum: list, path: str) -> Any: +def is_valid_data_field( + value: Any, nxdl_type: str, nxdl_enum: list, nxdl_enum_open: bool, path: str +) -> Any: # todo: Check this function and write test for it. It seems the function is not # working as expected. """Checks whether a given value is valid according to the type defined in the NXDL. @@ -688,11 +707,18 @@ def is_valid_data_field(value: Any, nxdl_type: str, nxdl_enum: list, path: str) # Check enumeration if nxdl_enum is not None and value not in nxdl_enum: - collector.collect_and_log( - path, - ValidationProblem.InvalidEnum, - nxdl_enum, - ) + if nxdl_enum_open: + collector.collect_and_log( + path, + ValidationProblem.OpenEnumWithNewItem, + nxdl_enum, + ) + else: + collector.collect_and_log( + path, + ValidationProblem.InvalidEnum, + nxdl_enum, + ) return value @@ -871,8 +897,8 @@ def update_and_warn(key: str, value: str): "https://github.com/FAIRmat-NFDI/nexus_definitions/" f"blob/{get_nexus_version_hash()}", ) - update_and_warn("/@NeXus_version", get_nexus_version()) - update_and_warn("/@HDF5_version", ".".join(map(str, h5py.h5.get_libversion()))) + update_and_warn("/@NeXus_release", get_nexus_version()) + update_and_warn("/@HDF5_Version", ".".join(map(str, h5py.h5.get_libversion()))) update_and_warn("/@h5py_version", h5py.__version__) diff --git a/src/pynxtools/dataconverter/nexus_tree.py b/src/pynxtools/dataconverter/nexus_tree.py index 77349df49..1b721cadb 100644 --- a/src/pynxtools/dataconverter/nexus_tree.py +++ b/src/pynxtools/dataconverter/nexus_tree.py @@ -35,19 +35,23 @@ from anytree.node.nodemixin import NodeMixin from pynxtools.dataconverter.helpers import ( - contains_uppercase, get_all_parents_for, get_nxdl_root_and_path, + is_variadic, is_appdef, remove_namespace_from_tag, ) -from pynxtools.definitions.dev_tools.utils.nxdl_utils import get_nx_namefit +from pynxtools.definitions.dev_tools.utils.nxdl_utils import ( + get_nx_namefit, + is_name_type, +) NexusType = Literal[ "NX_BINARY", "NX_BOOLEAN", "NX_CCOMPLEX", "NX_CHAR", + "NX_CHAR_OR_NUMBER", "NX_COMPLEX", "NX_DATE_TIME", "NX_FLOAT", @@ -111,6 +115,9 @@ class NexusNode(NodeMixin): The name of the node. type (Literal["group", "field", "attribute", "choice"]): The type of the node, e.g., xml tag in the nxdl file. + name_type (Optional["specified", "any", "partial"]): + The nameType of the node. + Defaults to "specified". optionality (Literal["required", "recommended", "optional"], optional): The optionality of the node. This is automatically set on init (in the respective subclasses) @@ -118,8 +125,8 @@ class NexusNode(NodeMixin): Defaults to "required". variadic (bool): True if the node name is variadic and can be matched against multiple names. - This is set automatically on init and will be True if the name contains - any uppercase characets and False otherwise. + This is set automatically on init and will be True if the `nameTYPE` is "any" + or "partial" and False otherwise. Defaults to False. inheritance (List[InstanceOf[ET._Element]]): The inheritance chain of the node. @@ -145,6 +152,7 @@ class NexusNode(NodeMixin): name: str type: Literal["group", "field", "attribute", "choice"] + name_type: Optional[Literal["specified", "any", "partial"]] = "specified" optionality: Literal["required", "recommended", "optional"] = "required" variadic: bool = False inheritance: List[ET._Element] @@ -177,6 +185,7 @@ def __init__( self, name: str, type: Literal["group", "field", "attribute", "choice"], + name_type: Optional[Literal["specified", "any", "partial"]] = "specified", optionality: Literal["required", "recommended", "optional"] = "required", variadic: Optional[bool] = None, parent: Optional["NexusNode"] = None, @@ -185,8 +194,9 @@ def __init__( super().__init__() self.name = name self.type = type + self.name_type = name_type self.optionality = optionality - self.variadic = contains_uppercase(self.name) + self.variadic = is_variadic(self.name, self.name_type) if variadic is not None: self.variadic = variadic if inheritance is not None: @@ -460,6 +470,7 @@ def _build_inheritance_chain(self, xml_elem: ET._Element) -> List[ET._Element]: This represents the direct field or group inside the specific xml file. """ name = xml_elem.attrib.get("name") + inheritance_chain = [xml_elem] inheritance = iter(self.inheritance) for elem in inheritance: @@ -483,18 +494,28 @@ def _build_inheritance_chain(self, xml_elem: ET._Element) -> List[ET._Element]: best_group = None best_score = -1 for group in groups: - if name in group.attrib and not contains_uppercase( - group.attrib["name"] - ): - continue group_name = ( group.attrib.get("name") if "name" in group.attrib else group.attrib["type"][2:].upper() ) - score = get_nx_namefit(name, group_name) - if get_nx_namefit(name, group_name) >= best_score: + if "name" in group.attrib: + group_name_type = group.attrib.get("nameType", "specified") + + else: + group_name_type = group.attrib.get("nameType", "any") + + if not is_variadic(group_name, group_name_type): + continue + + group_name_any = is_name_type(group, "any") + group_name_partial = is_name_type(group, "partial") + + score = get_nx_namefit( + name, group_name, group_name_any, group_name_partial + ) + if score >= best_score: best_group = group best_score = score @@ -525,21 +546,31 @@ def add_node_from(self, xml_elem: ET._Element) -> Optional["NexusNode"]: """ default_optionality = "required" if is_appdef(xml_elem) else "optional" tag = remove_namespace_from_tag(xml_elem.tag) + + name_type = xml_elem.attrib.get("nameType", "specified") + if tag in ("field", "attribute"): name = xml_elem.attrib.get("name") + current_elem = NexusEntity( parent=self, name=name, + name_type=name_type, type=tag, optionality=default_optionality, ) elif tag == "group": - name = xml_elem.attrib.get("name", xml_elem.attrib["type"][2:].upper()) + name = xml_elem.attrib.get("name") + if not name: + name = xml_elem.attrib["type"][2:].upper() + name_type = "any" + inheritance_chain = self._build_inheritance_chain(xml_elem) current_elem = NexusGroup( parent=self, type=tag, name=name, + name_type=name_type, nx_class=xml_elem.attrib["type"], inheritance=inheritance_chain, optionality=default_optionality, @@ -548,7 +579,7 @@ def add_node_from(self, xml_elem: ET._Element) -> Optional["NexusNode"]: current_elem = NexusChoice( parent=self, name=xml_elem.attrib["name"], - variadic=contains_uppercase(xml_elem.attrib["name"]), + name_type=name_type, optionality=default_optionality, ) else: @@ -644,6 +675,7 @@ def _check_sibling_namefit(self): for elem in self.inheritance[1:]: parent = elem.getparent() + if parent is None: continue siblings = parent.findall( @@ -651,15 +683,33 @@ def _check_sibling_namefit(self): ) for sibling in siblings: - sibling_name = sibling.attrib.get( - "name", sibling.attrib["type"][2:].upper() + sibling_name = ( + sibling.attrib.get("name") + if "name" in sibling.attrib + else sibling.attrib["type"][2:].upper() ) - if sibling_name == self.name or not contains_uppercase(sibling_name): + + if "name" in sibling.attrib: + sibling_name_type = sibling.attrib.get("nameType", "specified") + else: + sibling_name_type = sibling.attrib.get("nameType", "any") + + if not is_variadic(sibling_name, sibling_name_type): continue - if get_nx_namefit(self.name, sibling_name) < 0: + + sibling_name_any = is_name_type(sibling, "any") + sibling_name_partial = is_name_type(sibling, "partial") + + if ( + get_nx_namefit( + self.name, sibling_name, sibling_name_any, sibling_name_partial + ) + < 0 + ): continue sibling_node = self.parent.get_child_for(sibling) + if sibling_node is None: sibling_node = self.parent.add_node_from(sibling) self.is_a.append(sibling_node) @@ -717,9 +767,9 @@ def __init__(self, nx_class: str, **data) -> None: self._check_sibling_namefit() def __repr__(self) -> str: - return ( - f"{self.nx_class[2:].upper()}[{self.name.lower()}] ({self.optionality[:3]})" - ) + if self.type == "attribute": + return f"@{self.name} ({self.optionality[:3]})" + return f"{self.name} ({self.optionality[:3]})" class NexusEntity(NexusNode): @@ -747,6 +797,10 @@ class NexusEntity(NexusNode): Also the base classes of these entities are considered. If there is no restriction this is set to None. Defaults to None. + open_enum (bool): + If enumerations are used, the enumeration can be open (i.e., the value is not limited + to the enumeration items) or closed (i.e., the value must exactly match one of the + enumeration items). This is controlled by the open_enum boolean. By default, it is closed. shape (Optional[Tuple[Optional[int], ...]]): The shape of the entity as given by the dimensions tag. This is set automatically on init based on the values found in the nxdl file. @@ -761,7 +815,8 @@ class NexusEntity(NexusNode): type: Literal["field", "attribute"] unit: Optional[NexusUnitCategory] = None dtype: NexusType = "NX_CHAR" - items: Optional[List[Any]] = None + items: Optional[List[str]] = None + open_enum: bool = False shape: Optional[Tuple[Optional[int], ...]] = None def _set_type(self): @@ -784,7 +839,7 @@ def _set_unit(self): self.unit = elem.attrib["units"] return - def _set_items(self): + def _set_items_and_enum_type(self): """ Sets the enumeration items of the current entity based on the values in the inheritance chain. @@ -792,7 +847,10 @@ def _set_items(self): """ for elem in self.inheritance: enum = elem.find(f"nx:enumeration", namespaces=namespaces) + if enum is not None: + if enum.attrib.get("open") == "true": + self.open_enum = True self.items = [] for items in enum.findall(f"nx:item", namespaces=namespaces): value = items.attrib["value"] @@ -850,7 +908,7 @@ def __init__(self, **data) -> None: self._construct_inheritance_chain_from_parent() self._set_unit() self._set_type() - self._set_items() + self._set_items_and_enum_type() self._set_optionality() self._set_shape() @@ -919,6 +977,7 @@ def add_children_to(parent: NexusNode, xml_elem: ET._Element) -> None: name=appdef_xml_root.attrib["name"], nx_class="NXroot", type="group", + name_type="specified", optionality="required", variadic=False, parent=None, diff --git a/src/pynxtools/dataconverter/readers/example/reader.py b/src/pynxtools/dataconverter/readers/example/reader.py index 7e368a264..0d60b335e 100644 --- a/src/pynxtools/dataconverter/readers/example/reader.py +++ b/src/pynxtools/dataconverter/readers/example/reader.py @@ -57,7 +57,15 @@ def read( # The entries in the template dict should correspond with what the dataconverter # outputs with --generate-template for a provided NXDL file if ( - k.startswith("/ENTRY[entry]/required_group") + k.startswith( + ( + "/ENTRY[entry]/required_group", + "/ENTRY[entry]/specified_group", + "/ENTRY[entry]/any_groupGROUP[any_groupgroup]", + "/ENTRY[entry]/identified_calibration", + "/ENTRY[entry]/named_collection", + ) + ) or k in ( "/ENTRY[entry]/optional_parent/req_group_in_opt_group", diff --git a/src/pynxtools/dataconverter/validation.py b/src/pynxtools/dataconverter/validation.py index 250a87bf4..f923f11bf 100644 --- a/src/pynxtools/dataconverter/validation.py +++ b/src/pynxtools/dataconverter/validation.py @@ -112,9 +112,9 @@ def default_to_regular_dict(d): def split_class_and_name_of(name: str) -> Tuple[Optional[str], str]: """ Return the class and the name of a data dict entry of the form - `get_class_of("ENTRY[entry]")`, which will return `("ENTRY", "entry")`. + `split_class_and_name_of("ENTRY[entry]")`, which will return `("ENTRY", "entry")`. If this is a simple string it will just return this string, i.e. - `get_class_of("entry")` will return `None, "entry"`. + `split_class_and_name_of("entry")` will return `None, "entry"`. Args: name (str): The data dict entry @@ -161,7 +161,10 @@ def best_namefit_of(name: str, nodes: Iterable[NexusNode]) -> Optional[NexusNode if instance_name == node.name: return node - score = get_nx_namefit(instance_name, node.name) + name_any = node.name_type == "any" + name_partial = node.name_type == "partial" + + score = get_nx_namefit(instance_name, node.name, name_any, name_partial) if score > -1: best_match = node @@ -201,11 +204,19 @@ def get_variations_of(node: NexusNode, keys: Mapping[str, Any]) -> List[str]: return [f"{convert_nexus_to_caps(node.nx_class)}[{node.name}]"] variations = [] + for key in keys: concept_name, instance_name = split_class_and_name_of(key) if node.type == "attribute": # Remove the starting @ from attributes + if concept_name: + concept_name = ( + concept_name[1:] + if concept_name.startswith("@") + else concept_name + ) + instance_name = ( instance_name[1:] if instance_name.startswith("@") @@ -215,8 +226,11 @@ def get_variations_of(node: NexusNode, keys: Mapping[str, Any]) -> List[str]: if not concept_name or concept_name != node.name: continue + name_any = node.name_type == "any" + name_partial = node.name_type == "partial" + if ( - get_nx_namefit(instance_name, node.name) >= 0 + get_nx_namefit(instance_name, node.name, name_any, name_partial) >= 0 and key not in node.parent.get_all_direct_children_names() ): variations.append(key) @@ -228,8 +242,12 @@ def get_variations_of(node: NexusNode, keys: Mapping[str, Any]) -> List[str]: return variations def get_field_attributes(name: str, keys: Mapping[str, Any]) -> Mapping[str, Any]: + prefix = f"{name}@" return { - f"@{k.split('@')[1]}": keys[k] for k in keys if k.startswith(f"{name}@") + # Preserve everything after the field name, keeping '@attr[@attr]' or '@attr' + k[len(prefix) - 1 :]: v + for k, v in keys.items() + if k.startswith(prefix) } def handle_nxdata(node: NexusGroup, keys: Mapping[str, Any], prev_path: str): @@ -350,6 +368,7 @@ def check_nxdata(): def handle_group(node: NexusGroup, keys: Mapping[str, Any], prev_path: str): variants = get_variations_of(node, keys) + if node.parent_of: for child in node.parent_of: variants += get_variations_of(child, keys) @@ -377,9 +396,11 @@ def handle_group(node: NexusGroup, keys: Mapping[str, Any], prev_path: str): ValidationProblem.ExpectedGroup, None, ) - return + continue if node.nx_class == "NXdata": handle_nxdata(node, keys[variant], prev_path=f"{prev_path}/{variant}") + if node.nx_class == "NXcollection": + return else: recurse_tree(node, keys[variant], prev_path=f"{prev_path}/{variant}") @@ -449,6 +470,7 @@ def handle_field(node: NexusNode, keys: Mapping[str, Any], prev_path: str): mapping[f"{prev_path}/{variant}"], node.dtype, node.items, + node.open_enum, f"{prev_path}/{variant}", ) @@ -475,6 +497,7 @@ def handle_field(node: NexusNode, keys: Mapping[str, Any], prev_path: str): def handle_attribute(node: NexusNode, keys: Mapping[str, Any], prev_path: str): full_path = remove_from_not_visited(f"{prev_path}/@{node.name}") variants = get_variations_of(node, keys) + if ( not variants and node.optionality == "required" @@ -492,6 +515,7 @@ def handle_attribute(node: NexusNode, keys: Mapping[str, Any], prev_path: str): ], node.dtype, node.items, + node.open_enum, f"{prev_path}/{variant if variant.startswith('@') else f'@{variant}'}", ) @@ -538,17 +562,30 @@ def add_best_matches_for(key: str, node: NexusNode) -> Optional[NexusNode]: def is_documented(key: str, tree: NexusNode) -> bool: if mapping.get(key) is None: - # This value is not really set. Skip checking it's documentation. - return True - - # TODO remove when nameType is implemented - if key.endswith("/@URL"): + # This value is not really set. Skip checking its documentation. return True node = add_best_matches_for(key, tree) + if node is None: + key_path = key.replace("@", "") + while "/" in key_path: + key_path = key_path.rsplit("/", 1)[0] # Remove last segment + parent_node = add_best_matches_for(key_path, tree) + if ( + parent_node + and parent_node.type == "group" + and parent_node.nx_class == "NXcollection" + ): + # Collection found for parents, mark as documented + return True + return False + if node.type == "group" and node.nx_class == "NXcollection": + # Collection found, mark as documented + return True + if isinstance(mapping[key], dict) and "link" in mapping[key]: # TODO: Follow link and check consistency with current field return True @@ -562,7 +599,9 @@ def is_documented(key: str, tree: NexusNode) -> bool: # We still do some further checks before returning. # Check general validity - mapping[key] = is_valid_data_field(mapping[key], node.dtype, node.items, key) + mapping[key] = is_valid_data_field( + mapping[key], node.dtype, node.items, node.open_enum, key + ) # Check main field exists for units if ( @@ -588,6 +627,7 @@ def recurse_tree( keys = _follow_link(keys, prev_path) if keys is None: return + handling_map.get(child.type, handle_unknown_type)(child, keys, prev_path) def check_attributes_of_nonexisting_field( @@ -805,12 +845,29 @@ def startswith_with_variations( else: # check that parent has units node = add_best_matches_for(not_visited_key.rsplit("/", 1)[0], tree) + + # Search if unit is somewhere in an NXcollection + if node is None: + key_path = not_visited_key.replace("@", "") + while "/" in key_path: + key_path = key_path.rsplit("/", 1)[0] # Remove last segment + parent_node = add_best_matches_for(key_path, tree) + if ( + parent_node + and parent_node.type == "group" + and parent_node.nx_class == "NXcollection" + ): + # NXcollection found → break while, continue outer loop + break + continue + if node is None or node.type != "field" or node.unit is None: - collector.collect_and_log( - not_visited_key, - ValidationProblem.UnitWithoutDocumentation, - mapping[not_visited_key], - ) + if not ignore_undocumented: + collector.collect_and_log( + not_visited_key, + ValidationProblem.UnitWithoutDocumentation, + mapping[not_visited_key], + ) # parent key will be checked on its own if it exists, because it is in the list continue diff --git a/src/pynxtools/definitions b/src/pynxtools/definitions index f7ba53f4f..a85e10cd0 160000 --- a/src/pynxtools/definitions +++ b/src/pynxtools/definitions @@ -1 +1 @@ -Subproject commit f7ba53f4fb409b03fde6af6ccf29146392a2c142 +Subproject commit a85e10cd0289f4e44b0fec011ff54703e6705383 diff --git a/src/pynxtools/nexus-version.txt b/src/pynxtools/nexus-version.txt index 48133eb9b..c137d59d6 100644 --- a/src/pynxtools/nexus-version.txt +++ b/src/pynxtools/nexus-version.txt @@ -1 +1 @@ -v2024.02-1885-gdb54d2a0 \ No newline at end of file +v2024.02-1913-ga85e10cd \ No newline at end of file diff --git a/src/pynxtools/nomad/schema.py b/src/pynxtools/nomad/schema.py index cb0b4325e..f9fc1c4ec 100644 --- a/src/pynxtools/nomad/schema.py +++ b/src/pynxtools/nomad/schema.py @@ -39,6 +39,7 @@ from nomad.normalizing.common import nomad_atoms_from_ase_atoms from nomad.normalizing.topology import add_system, add_system_info from scipy.spatial import cKDTree +import pint try: from nomad import utils @@ -139,9 +140,7 @@ class NexusActivityStep(ActivityStep): class AnchoredReference(EntityReference): - def normalize(self, archive, logger): - def create_Entity(lab_id, archive, f_name, qunt_name): entitySec = Entity() entitySec.lab_id = lab_id @@ -197,14 +196,16 @@ def normalize(self, archive, logger): if not (val := getattr(self, identifier)): continue # identifier_path = f"{self.m_def.name}_{identifier.split('__field')[0]}" - field_n = identifier.split('__field')[0] + field_n = identifier.split("__field")[0] logger.info(f"Lab id {val} to be created") nx_id = AnchoredReference(lab_id=val, name=field_n) nx_id.m_set_section_attribute( - "m_nx_data_path", self.m_get_quantity_attribute(identifier, "m_nx_data_path") + "m_nx_data_path", + self.m_get_quantity_attribute(identifier, "m_nx_data_path"), ) nx_id.m_set_section_attribute( - "m_nx_data_file", self.m_get_quantity_attribute(identifier, "m_nx_data_file") + "m_nx_data_file", + self.m_get_quantity_attribute(identifier, "m_nx_data_file"), ) self.AnchoredReferences.append(nx_id) @@ -510,12 +511,12 @@ def nxdata_ensure_definition( else: filters = ["DATA", "AXISNAME", "FIELDNAME_errors"] # get the reduced options - newdefintions = {} + newdefinitions = {} for dname, definition in self.m_def.all_aliases: if dname not in filters: - newdefintions[dname] = definition + newdefinitions[dname] = definition # run the query - definition = resolve_variadic_name(newdefintions, def_or_name, hint) + definition = resolve_variadic_name(newdefinitions, def_or_name, hint) return definition return super(current_cls, self)._ensure_definition( def_or_name, @@ -555,9 +556,11 @@ def __get_enumeration(xml_node: ET.Element) -> Tuple[Optional[MEnum], Optional[b return None, None items = enumeration.findall("nx:item", __XML_NAMESPACES) - open = bool(enumeration.attrib["open"]) if "open" in enumeration.attrib else False + open_enum = ( + bool(enumeration.attrib["open"]) if "open" in enumeration.attrib else False + ) - return MEnum([value.attrib["value"] for value in items]), open + return MEnum([value.attrib["value"] for value in items]), open_enum def __add_common_properties(xml_node: ET.Element, definition: Definition): @@ -765,11 +768,23 @@ def __create_field(xml_node: ET.Element, container: Section) -> Quantity: # dimensionality nx_dimensionality = xml_attrs.get("units", None) if nx_dimensionality: - if nx_dimensionality not in NXUnitSet.mapping: - raise NotImplementedError( - f"Unit {nx_dimensionality} is not supported for {name}." - ) - dimensionality = NXUnitSet.mapping[nx_dimensionality] + dimensionality = NXUnitSet.mapping.get(nx_dimensionality) + if not dimensionality and nx_dimensionality != "NX_ANY": + try: + from nomad.units import ureg + + quantity = 1 * ureg(nx_dimensionality) + if quantity.dimensionality == "dimensionless": + dimensionality = "1" + else: + dimensionality = str(quantity.dimensionality) + except ( + pint.errors.UndefinedUnitError, + pint.errors.DefinitionSyntaxError, + ) as err: + raise NotImplementedError( + f"Unit {nx_dimensionality} is not supported for {name}." + ) from err else: dimensionality = None diff --git a/src/pynxtools/testing/nexus_conversion.py b/src/pynxtools/testing/nexus_conversion.py index 32c5afb0f..2f8e99241 100644 --- a/src/pynxtools/testing/nexus_conversion.py +++ b/src/pynxtools/testing/nexus_conversion.py @@ -20,7 +20,7 @@ import logging import os from glob import glob -from typing import Dict, List, Literal, Tuple +from typing import Dict, List, Literal, Tuple, Optional try: from nomad.client import parse @@ -30,13 +30,11 @@ NOMAD_AVAILABLE = False -from pynxtools.dataconverter.convert import get_reader, transfer_data_into_template +from pynxtools.dataconverter.convert import get_reader, convert from pynxtools.dataconverter.helpers import ( add_default_root_attributes, get_nxdl_root_and_path, ) -from pynxtools.dataconverter.validation import validate_dict_against -from pynxtools.dataconverter.writer import Writer from pynxtools.nexus.nexus import HandleNexus @@ -115,7 +113,11 @@ def convert_to_nexus( self.ref_nexus_file = [file for file in example_files if file.endswith(".nxs")][ 0 ] - input_files = [file for file in example_files if not file.endswith(".nxs")] + input_files = [ + file + for file in example_files + if not file.endswith((".nxs", "ref_output.txt")) + ] assert self.ref_nexus_file, "Reference nexus (.nxs) file not found" assert ( @@ -126,28 +128,40 @@ def convert_to_nexus( nxdl_root, nxdl_file = get_nxdl_root_and_path(self.nxdl) assert os.path.exists(nxdl_file), f"NXDL file {nxdl_file} not found" - read_data = transfer_data_into_template( - input_file=input_files, - reader=self.reader_name, - nxdl_name=self.nxdl, - nxdl_root=nxdl_root, - skip_verify=True, - **self.kwargs, - ) - - # Clear the log of `transfer_data_into_template` + # Clear the log of `convert` self.caplog.clear() with self.caplog.at_level(caplog_level): - _ = validate_dict_against( - self.nxdl, read_data, ignore_undocumented=ignore_undocumented - )[0] - assert self.caplog.text == "" + _ = convert( + input_file=tuple(input_files), + reader=self.reader_name, + nxdl=self.nxdl, + skip_verify=False, + ignore_undocumented=ignore_undocumented, + output=self.created_nexus, + **self.kwargs, + ) - add_default_root_attributes( - data=read_data, filename=os.path.basename(self.created_nexus) - ) - Writer(read_data, nxdl_file, self.created_nexus).write() + test_output = self.caplog.messages + + files_with_expected_output = [ + file for file in example_files if file.endswith("ref_output.txt") + ] + + if files_with_expected_output: + output_file = files_with_expected_output[0] + with open(output_file, "r") as file: + expected_messages = [line.strip() for line in file.readlines()] + + for message in expected_messages: + if caplog_level == "WARNING": + if message.startswith(("WARNING", "ERROR")): + test_output.remove(message) + if caplog_level == "ERROR": + if message.startswith("ERROR"): + test_output.remove(message) + + assert test_output == [] if NOMAD_AVAILABLE: kwargs = dict( @@ -172,7 +186,7 @@ def check_reproducibility_of_nexus(self, **kwargs): ] IGNORE_SECTIONS: Dict[str, List[str]] = { **reader_ignore_sections, - "ATTRS (//@HDF5_version)": ["DEBUG - value:"], + "ATTRS (//@HDF5_Version)": ["DEBUG - value:"], "ATTRS (//@file_name)": ["DEBUG - value:"], "ATTRS (//@file_time)": ["DEBUG - value:"], "ATTRS (//@file_update_time)": ["DEBUG - value:"], diff --git a/tests/data/dataconverter/readers/example/testdata.json b/tests/data/dataconverter/readers/example/testdata.json index dbcf035a3..bfeb4d1e8 100644 --- a/tests/data/dataconverter/readers/example/testdata.json +++ b/tests/data/dataconverter/readers/example/testdata.json @@ -15,6 +15,7 @@ "definition_version": "0.0.1", "program_name": "Nexus Parser", "type": "2nd type", + "type2": "2nd type", "date_value": "2022-01-22T12:14:12.05018+00:00", "date_value_units": "", "required_child": 1, diff --git a/tests/data/nexus/NXtest2.nxdl.xml b/tests/data/nexus/NXtest2.nxdl.xml deleted file mode 100644 index 7b33b2165..000000000 --- a/tests/data/nexus/NXtest2.nxdl.xml +++ /dev/null @@ -1,455 +0,0 @@ - - - - - - - Characterization of a sample during a session on an electron microscope. - - - - - - - - Metadata and numerical data of the microscope and the lab in which it stands. - - - - - - Given name of the microscope at the hosting institution. This is an alias. - Examples could be NionHermes, Titan, JEOL, Gemini, etc. - - - - - Location of the lab or place where the instrument is installed. - Using GEOREF is preferred. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If the lens is described at least one of the fields - voltage, current, or value should be defined. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Description of the type of the detector. - - Electron microscopes have typically multiple detectors. - Different technologies are in use like CCD, scintillator, - direct electron, CMOS, or image plate to name but a few. - - - - Instrument-specific alias/name - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A container for storing a set of NXevent_data_em instances. - - - - - \ No newline at end of file diff --git a/tests/data/nexus/Ref_nexus_test.log b/tests/data/nexus/Ref_nexus_test.log index 54541649f..7f7279708 100644 --- a/tests/data/nexus/Ref_nexus_test.log +++ b/tests/data/nexus/Ref_nexus_test.log @@ -32,24 +32,34 @@ DEBUG - classpath: ['NXentry'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY NXentry.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY): DEBUG - DEBUG - documentation (NXentry.nxdl.xml:): DEBUG - - (**required**) :ref:`NXentry` describes the measurement. - - The top-level NeXus group which contains all the data and associated - information that comprise a single measurement. - It is mandatory that there is at least one - group of this type in the NeXus file. - + (**required**) :ref:`NXentry` describes the measurement. + + The top-level NeXus group which contains all the data and associated + information that comprise a single measurement. + It is mandatory that there is at least one + group of this type in the NeXus file. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry@NX_class) DEBUG - value: NXentry DEBUG - classpath: ['NXentry'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY NXentry.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/collection_time): @@ -60,9 +70,9 @@ NXentry.nxdl.xml:/collection_time DEBUG - <> DEBUG - documentation (NXentry.nxdl.xml:/collection_time): DEBUG - - Time transpired actually collecting data i.e. taking out time when collection was - suspended due to e.g. temperature out of range - + Time transpired actually collecting data i.e. taking out time when collection was + suspended due to e.g. temperature out of range + DEBUG - ===== ATTRS (//entry/collection_time@units) DEBUG - value: s DEBUG - classpath: ['NXentry', 'NX_FLOAT'] @@ -74,158 +84,225 @@ DEBUG - classpath: ['NXentry', 'NXdata'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/DATA NXentry.nxdl.xml:/DATA +NXobject.nxdl.xml:/DATA NXdata.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/DATA): DEBUG - DEBUG - documentation (NXentry.nxdl.xml:/DATA): DEBUG - - The data group - - .. note:: Before the NIAC2016 meeting [#]_, at least one - :ref:`NXdata` group was required in each :ref:`NXentry` group. - At the NIAC2016 meeting, it was decided to make :ref:`NXdata` - an optional group in :ref:`NXentry` groups for data files that - do not use an application definition. - It is recommended strongly that all NeXus data files provide - a NXdata group. - It is permissable to omit the NXdata group only when - defining the default plot is not practical or possible - from the available data. - - For example, neutron event data may not have anything that - makes a useful plot without extensive processing. - - Certain application definitions override this decision and - require an :ref:`NXdata` group - in the :ref:`NXentry` group. The ``minOccurs=0`` attribute - in the application definition will indicate the - :ref:`NXdata` group - is optional, otherwise, it is required. - - .. [#] NIAC2016: - https://www.nexusformat.org/NIAC2016.html, - https://github.com/nexusformat/NIAC/issues/16 - + The data group + + .. note:: Before the NIAC2016 meeting [#]_, at least one + :ref:`NXdata` group was required in each :ref:`NXentry` group. + At the NIAC2016 meeting, it was decided to make :ref:`NXdata` + an optional group in :ref:`NXentry` groups for data files that + do not use an application definition. + It is recommended strongly that all NeXus data files provide + a NXdata group. + It is permissible to omit the NXdata group only when + defining the default plot is not practical or possible + from the available data. + + For example, neutron event data may not have anything that + makes a useful plot without extensive processing. + + Certain application definitions override this decision and + require an :ref:`NXdata` group + in the :ref:`NXentry` group. The ``minOccurs=0`` attribute + in the application definition will indicate the + :ref:`NXdata` group + is optional, otherwise, it is required. + + .. [#] NIAC2016: + https://www.nexusformat.org/NIAC2016.html, + https://github.com/nexusformat/NIAC/issues/16 + + +DEBUG - documentation (NXobject.nxdl.xml:/DATA): +DEBUG - DEBUG - documentation (NXdata.nxdl.xml:): DEBUG - - :ref:`NXdata` describes the plottable data and related dimension scales. - - .. index:: plotting - - It is strongly recommended that there is at least one :ref:`NXdata` - group in each :ref:`NXentry` group. - Note that the fields named ``AXISNAME`` and ``DATA`` - can be defined with different names. - (Upper case is used to indicate that the actual name is left to the user.) - The ``signal`` and ``axes`` attributes of the - ``data`` group define which items - are plottable data and which are *dimension scales*, respectively. - - :ref:`NXdata` is used to implement one of the basic motivations in NeXus, - to provide a default plot for the data of this :ref:`NXentry`. The actual data - might be stored in another group and (hard) linked to the :ref:`NXdata` group. - - * Each :ref:`NXdata` group will define one field as the default - plottable data. The value of the ``signal`` attribute names this field. - Additional fields may be used to describe the dimension scales and - uncertainities. - The ``auxiliary_signals`` attribute is a list of the other fields - to be plotted with the ``signal`` data. - * The plottable data may be of arbitrary rank up to a maximum - of ``NX_MAXRANK=32`` (for compatibility with backend file formats). - * The plottable data will be named as the value of - the group ``signal`` attribute, such as:: - - data:NXdata - @signal = "counts" - @axes = "mr" - @mr_indices = 0 - counts: float[100] --> the default dependent data - mr: float[100] --> the default independent data - - The field named in the ``signal`` attribute **must** exist, either - directly as a NeXus field or defined through a link. - - * The group ``axes`` attribute will name the - *dimension scale* associated with the plottable data. - - If available, the standard deviations of the data are to be - stored in a data set of the same rank and dimensions, with the name ``errors``. - - * For each data dimension, there should be a one-dimensional array - of the same length. - * These one-dimensional arrays are the *dimension scales* of the - data, *i.e*. the values of the independent variables at which the data - is measured, such as scattering angle or energy transfer. - - .. index:: link - .. index:: axes (attribute) - - The preferred method to associate each data dimension with - its respective dimension scale is to specify the field name - of each dimension scale in the group ``axes`` attribute as a string list. - Here is an example for a 2-D data set *data* plotted - against *time*, and *pressure*. (An additional *temperature* data set - is provided and could be selected as an alternate for the *pressure* axis.):: - - data_2d:NXdata - @signal="data" - @axes=["time", "pressure"] - @pressure_indices=1 - @temperature_indices=1 - @time_indices=0 - data: float[1000,20] - pressure: float[20] - temperature: float[20] - time: float[1000] - - .. rubric:: Old methods to identify the plottable data - - There are two older methods of associating - each data dimension to its respective dimension scale. - Both are now out of date and - should not be used when writing new data files. - However, client software should expect to see data files - written with any of these methods. - - * One method uses the ``axes`` - attribute to specify the names of each *dimension scale*. - - * The oldest method uses the ``axis`` attribute on each - *dimension scale* to identify - with an integer the axis whose value is the number of the dimension. - - .. index: !plot; axis label - plot, axis units - units - dimension scale - - Each axis of the plot may be labeled with information from the - dimension scale for that axis. The optional ``@long_name`` attribute - is provided as the axis label default. If ``@long_name`` is not - defined, then use the name of the dimension scale. A ``@units`` attribute, - if available, may be added to the axis label for further description. - See the section :ref:`Design-Units` for more information. - - .. index: !plot; axis title - - The optional ``title`` field, if available, provides a suggested - title for the plot. If no ``title`` field is found in the :ref:`NXdata` - group, look for a ``title`` field in the parent :ref:`NXentry` group, - with a fallback to displaying the path to the :ref:`NXdata` group. - - NeXus is about how to find and annotate the data to be plotted - but not to describe how the data is to be plotted. - (https://www.nexusformat.org/NIAC2018Minutes.html#nxdata-plottype--attribute) - + The :ref:`NXdata` class is designed to encapsulate all the information required for a set of data to be plotted. + NXdata groups contain plottable data (also referred to as *signals* or *dependent variables*) and their + associated axis coordinates (also referred to as *axes* or *independent variables*). + + The actual names of the :ref:`DATA ` and :ref:`AXISNAME ` fields + can be chosen :ref:`freely `, as indicated by the upper case (this is a common convention in all NeXus classes). + + .. note:: ``NXdata`` provides data and coordinates to be plotted but + does not describe how the data is to be plotted or even the dimensionality of the plot. + https://www.nexusformat.org/NIAC2018Minutes.html#nxdata-plottype--attribute + + .. include:: data/index.rst + :start-line: 1 + + .. admonition:: Example of a simple curve plot + + .. code-block:: + + data:NXdata + @signal = "data" + @axes = ["x"] + data: float[100] + x: float[100] + + More complex cases are supported + + * histogram data: ``x`` has one more value than ``data``. + * alternative axes: instead of a single ``x`` axis you can have several axes, one of which being the default. + * signals with more than one dimension: ``data`` could be 2D with axes ``x`` and ``y`` along each dimension. + * axes with more than one dimension: ``data`` could be 2D with axes ``x`` and ``y`` also being 2D, providing a + unique ``(x, y)`` coordinate for each ``data`` point. + + **Signals:** + + .. index:: plotting + + .. admonition:: Defined by + + * :ref:`DATA ` fields + * the :ref:`signal ` attribute + * the :ref:`auxiliary_signals` attribute + + The :ref:`DATA ` fields contain the signal values to be plotted. The name of the field + to be used as the *default plot signal* is provided by the :ref:`signal ` attribute. + The names of the fields to be used as *secondary plot signals* are provided by the + :ref:`auxiliary_signals` attribute. + + .. admonition:: An example with three signals, one of which being the default + + .. code-block:: + + data:NXdata + @signal = "data1" + @auxiliary_signals = ["data2", "data3"] + data1: float[10,20,30] # the default signal + data2: float[10,20,30] + data3: float[10,20,30] + + **Axes:** + + .. index:: axes (attribute) + .. index:: coordinates + + .. admonition:: Defined by + + * :ref:`AXISNAME ` fields + * the :ref:`axes ` attribute + * :ref:`AXISNAME_indices ` attributes + + The fields and attributes are defined as follows + + 1. The :ref:`AXISNAME ` fields contain the axis coordinates associated with the signal values. + + 2. The :ref:`axes ` attribute provides the names of the :ref:`AXISNAME ` + fields to be used as the `default axis` for each dimension of the :ref:`DATA ` fields. + + 3. The :ref:`AXISNAME_indices ` attributes describe the :ref:`DATA ` + dimensions spanned by the corresponding :ref:`AXISNAME ` fields. + + The fields and attributes have the following constraints + + 1. The length of the :ref:`axes ` attribute must be equal to the rank of the :ref:`DATA ` + fields. When a particular dimension has no default axis, the string “.” is used in that position. + + 2. The number of values in :ref:`AXISNAME_indices ` must be equal to the rank of the corresponding + :ref:`AXISNAME ` field. + + 3. When :ref:`AXISNAME_indices ` is missing for a given + :ref:`AXISNAME ` field, the positions of the :ref:`AXISNAME ` + field name in the :ref:`axes ` attribute are used. + + 4. When :ref:`AXISNAME_indices ` is the same as the indices of "AXISNAME" in the + :ref:`axes ` attribute, there is no need to provide + :ref:`AXISNAME_indices `. + + 5. The indices of "AXISNAME" in the :ref:`axes ` attribute must be a subset of + :ref:`AXISNAME_indices `. + + 6. The shape of an :ref:`AXISNAME ` field must correspond to the shape of the + :ref:`DATA ` dimensions it spans. This means that for each dimension ``i`` in ``[0, AXISNAME.ndim)`` + spanned by axis field :ref:`AXISNAME `, the number of axis values ``AXISNAME.shape[i]`` + along dimension ``i`` must be equal to the number of data points ``DATA.shape[AXISNAME_indices[i]]`` along dimension ``i`` + or one more than the number of data points ``DATA.shape[AXISNAME_indices[i]]+1`` in case the + :ref:`AXISNAME ` field contains histogram bin edges along dimension ``i``. + + Highlight consequences of these constraints + + 1. An :ref:`AXISNAME ` field can have more than one dimension and can therefore span + more than one :ref:`DATA ` dimension. Conversely, one :ref:`DATA ` dimension + can be spanned by more than one :ref:`AXISNAME ` field. The default axis name (if any) + of each dimension can be found in the :ref:`axes ` attribute. + + 2. A list of all available axes is not provided directly. All strings in the :ref:`axes ` attribute + (excluding the “.” string) are axis field names. In addition the prefix of an attribute ending with the string "_indices" is also + an axis field name. + + .. admonition:: The following example covers all axes features supported (see :ref:`sphx_glr_classes_base_classes_data_plot_fscan2d.py`) + + .. code-block:: + + data:NXdata + @signal = "data" + @axes = ["x_set", "y_set", "."] # default axes for all three dimensions + @x_encoder_indices = [0, 1] + @y_encoder_indices = 1 # or [1] + data: float[10,7,1024] + x_encoder: float[11,7] # coordinates along the first and second dimensions + y_encoder: float[7] # coordinates along the second dimension + x_set: float[10] # default coordinates along the first dimension + y_set: float[7] # default coordinates along the second dimension + + **Uncertainties:** + + .. admonition:: Defined by + + * :ref:`FIELDNAME_errors ` fields + + Standard deviations on data values as well as coordinates can be provided by + :ref:`FIELDNAME_errors ` fields where ``FIELDNAME`` is the name of a + :ref:`DATA ` field or an :ref:`AXISNAME ` field. + + .. admonition:: An example of uncertainties on the signal, auxiliary signals and axis coordinates + + .. code-block:: + + data:NXdata + @signal = "data1" + @auxiliary_signals = ["data2", "data3"] + @axes = ["x", ".", "z"] + data1: float[10,20,30] + data2: float[10,20,30] + data3: float[10,20,30] + x: float[10] + z: float[30] + data1_errors: float[10,20,30] + data2_errors: float[10,20,30] + data3_errors: float[10,20,30] + x_errors: float[10] + z_errors: float[30] + + +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/data@NX_class) DEBUG - value: NXdata DEBUG - classpath: ['NXentry', 'NXdata'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/DATA NXentry.nxdl.xml:/DATA +NXobject.nxdl.xml:/DATA NXdata.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== ATTRS (//entry/data@axes) @@ -234,96 +311,89 @@ DEBUG - classpath: ['NXentry', 'NXdata'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/DATA NXentry.nxdl.xml:/DATA +NXobject.nxdl.xml:/DATA NXdata.nxdl.xml: +NXobject.nxdl.xml: DEBUG - NXdata.nxdl.xml:@axes - [NX_CHAR] DEBUG - <> DEBUG - documentation (NXdata.nxdl.xml:/axes): DEBUG - - .. index:: plotting - - Array of strings holding the :ref:`names ` of - the independent data fields used in the default plot for all of - the dimensions of the :ref:`signal ` - as well as any :ref:`auxiliary signals `. - - One name is provided for every dimension in the *signal* or *auxiliary signal* fields. - - The *axes* values are the names of fields or links that *must* exist and be direct - children of this NXdata group. - - An axis slice is specified using a field named ``AXISNAME_indices`` - as described below (where the text shown here as ``AXISNAME`` is to be - replaced by the actual field name). - - When no default axis is available for a particular dimension - of the plottable data, use a "." in that position. - Such as:: - - @axes=["time", ".", "."] - - Since there are three items in the list, the *signal* field - must be a three-dimensional array (rank=3). The first dimension - is described by the values of a one-dimensional array named ``time`` - while the other two dimensions have no fields to be used as dimension scales. - - See examples provided on the NeXus wiki: - https://www.nexusformat.org/2014_axes_and_uncertainties.html - - If there are no axes at all (such as with a stack of images), - the axes attribute can be omitted. - + .. index:: plotting + + The ``axes`` attribute is a list of strings which are the names of the :ref:`AXISNAME ` fields + to be used as the default axis along every :ref:`DATA ` dimension. As a result the length must + be equal to the rank of the :ref:`DATA ` fields. The string "." can be used for + dimensions without a default axis. + + .. note:: When ``axes`` contains multiple strings, it must be saved as an actual array + of strings and not a single comma separated string. + DEBUG - ===== ATTRS (//entry/data@signal) DEBUG - value: data DEBUG - classpath: ['NXentry', 'NXdata'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/DATA NXentry.nxdl.xml:/DATA +NXobject.nxdl.xml:/DATA NXdata.nxdl.xml: +NXobject.nxdl.xml: DEBUG - NXdata.nxdl.xml:@signal - [NX_CHAR] DEBUG - <> DEBUG - documentation (NXdata.nxdl.xml:/signal): DEBUG - - .. index:: find the default plottable data - .. index:: plotting - .. index:: signal attribute value - - Declares which NeXus field is the default. - The value is the :ref:`name ` of the data field to be plotted. - This field or link *must* exist and be a direct child of this NXdata group. - - It is recommended (as of NIAC2014) to use this attribute - rather than adding a signal attribute to the field. - See https://www.nexusformat.org/2014_How_to_find_default_data.html - for a summary of the discussion. - + .. index:: find the default plottable data + .. index:: plotting + .. index:: signal attribute value + + The value is the :ref:`name ` of the signal that contains + the default plottable data. This field or link *must* exist and be a direct child + of this NXdata group. + + It is recommended (as of NIAC2014) to use this attribute + rather than adding a signal attribute to the field. + See https://www.nexusformat.org/2014_How_to_find_default_data.html + for a summary of the discussion. + DEBUG - ===== FIELD (//entry/data/angles): DEBUG - value: [-1.96735314 -1.91500657 -1.86266001 -1.81031344 -1.75796688 -1.70562031 ... -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - <> DEBUG - Dataset referenced as NXdata AXIS #0 DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME): DEBUG - - Dimension scale defining an axis of the data. - Client is responsible for defining the dimensions of the data. - The name of this field may be changed to fit the circumstances. - Standard NeXus client tools will use the attributes to determine - how to use this field. - + Coordinate values along one or more :ref:`DATA ` dimensions. + + The shape of an ``AXISNAME`` field must correspond to the shape of the :ref:`DATA ` + dimensions it spans. This means that for each ``i`` in ``[0, AXISNAME.ndim)`` the number of data points + ``DATA.shape[AXISNAME_indices[i]]`` must be equal to the number of coordinates ``AXISNAME.shape[i]`` or the + number of bin edges ``AXISNAME.shape[i]+1`` in case of histogram data. + + As the upper case ``AXISNAME`` indicates, the names of the ``AXISNAME`` fields can be chosen :ref:`freely `. + + Most ``AXISNAME`` fields will be sequences of numbers but if an axis is better represented using names, such as channel names, + an array of NX_CHAR can be provided. + DEBUG - ===== ATTRS (//entry/data/angles@target) DEBUG - value: /entry/instrument/analyser/angles -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - @target - IS NOT IN SCHEMA DEBUG - DEBUG - ===== ATTRS (//entry/data/angles@units) DEBUG - value: 1/Å -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME -DEBUG - NXdata.nxdl.xml:/AXISNAME@units - REQUIRED, but undefined unit category +DEBUG - NXdata.nxdl.xml:/AXISNAME@units - [NX_CHAR] +DEBUG - Dataset referenced as NXdata AXIS #0 +DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME/units): +DEBUG - + Unit in which the coordinate values are expressed. + See the section :ref:`Design-Units` for more information. + DEBUG - ===== FIELD (//entry/data/data): DEBUG - value: [[0. 0. 0. ... 0. 0. 0.] ... DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] @@ -333,15 +403,15 @@ DEBUG - <> DEBUG - Dataset referenced as NXdata SIGNAL DEBUG - documentation (NXdata.nxdl.xml:/DATA): DEBUG - - .. index:: plotting - - This field contains the data values to be used as the - NeXus *plottable data*. - Client is responsible for defining the dimensions of the data. - The name of this field may be changed to fit the circumstances. - Standard NeXus client tools will use the attributes to determine - how to use this field. - + .. index:: plotting + + Data values to be used as the NeXus *plottable data*. As the upper case ``DATA`` + indicates, the names of the ``DATA`` fields can be chosen :ref:`freely `. The :ref:`signal attribute ` + and :ref:`auxiliary_signals attribute` can be used to find all datasets in the ``NXdata`` + that contain data values. + + The maximum rank is ``32`` for compatibility with backend file formats. + DEBUG - ===== ATTRS (//entry/data/data@target) DEBUG - value: /entry/instrument/analyser/data DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] @@ -357,60 +427,84 @@ NXdata.nxdl.xml:/DATA DEBUG - NXdata.nxdl.xml:/DATA@units - REQUIRED, but undefined unit category DEBUG - ===== FIELD (//entry/data/delays): DEBUG - value: [-1.1 -1.08041237 -1.06082474 -1.04123711 -1.02164948 -1.00206186 ... -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - <> DEBUG - Dataset referenced as NXdata AXIS #2 DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME): DEBUG - - Dimension scale defining an axis of the data. - Client is responsible for defining the dimensions of the data. - The name of this field may be changed to fit the circumstances. - Standard NeXus client tools will use the attributes to determine - how to use this field. - + Coordinate values along one or more :ref:`DATA ` dimensions. + + The shape of an ``AXISNAME`` field must correspond to the shape of the :ref:`DATA ` + dimensions it spans. This means that for each ``i`` in ``[0, AXISNAME.ndim)`` the number of data points + ``DATA.shape[AXISNAME_indices[i]]`` must be equal to the number of coordinates ``AXISNAME.shape[i]`` or the + number of bin edges ``AXISNAME.shape[i]+1`` in case of histogram data. + + As the upper case ``AXISNAME`` indicates, the names of the ``AXISNAME`` fields can be chosen :ref:`freely `. + + Most ``AXISNAME`` fields will be sequences of numbers but if an axis is better represented using names, such as channel names, + an array of NX_CHAR can be provided. + DEBUG - ===== ATTRS (//entry/data/delays@target) DEBUG - value: /entry/instrument/analyser/delays -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - @target - IS NOT IN SCHEMA DEBUG - DEBUG - ===== ATTRS (//entry/data/delays@units) DEBUG - value: fs -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME -DEBUG - NXdata.nxdl.xml:/AXISNAME@units - REQUIRED, but undefined unit category +DEBUG - NXdata.nxdl.xml:/AXISNAME@units - [NX_CHAR] +DEBUG - Dataset referenced as NXdata AXIS #2 +DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME/units): +DEBUG - + Unit in which the coordinate values are expressed. + See the section :ref:`Design-Units` for more information. + DEBUG - ===== FIELD (//entry/data/energies): DEBUG - value: [ 2.5 2.46917808 2.43835616 2.40753425 2.37671233 2.34589041 ... -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - <> DEBUG - Dataset referenced as NXdata AXIS #1 DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME): DEBUG - - Dimension scale defining an axis of the data. - Client is responsible for defining the dimensions of the data. - The name of this field may be changed to fit the circumstances. - Standard NeXus client tools will use the attributes to determine - how to use this field. - + Coordinate values along one or more :ref:`DATA ` dimensions. + + The shape of an ``AXISNAME`` field must correspond to the shape of the :ref:`DATA ` + dimensions it spans. This means that for each ``i`` in ``[0, AXISNAME.ndim)`` the number of data points + ``DATA.shape[AXISNAME_indices[i]]`` must be equal to the number of coordinates ``AXISNAME.shape[i]`` or the + number of bin edges ``AXISNAME.shape[i]+1`` in case of histogram data. + + As the upper case ``AXISNAME`` indicates, the names of the ``AXISNAME`` fields can be chosen :ref:`freely `. + + Most ``AXISNAME`` fields will be sequences of numbers but if an axis is better represented using names, such as channel names, + an array of NX_CHAR can be provided. + DEBUG - ===== ATTRS (//entry/data/energies@target) DEBUG - value: /entry/instrument/analyser/energies -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME DEBUG - @target - IS NOT IN SCHEMA DEBUG - DEBUG - ===== ATTRS (//entry/data/energies@units) DEBUG - value: eV -DEBUG - classpath: ['NXentry', 'NXdata', 'NX_NUMBER'] +DEBUG - classpath: ['NXentry', 'NXdata', 'NX_CHAR_OR_NUMBER'] DEBUG - classes: NXdata.nxdl.xml:/AXISNAME -DEBUG - NXdata.nxdl.xml:/AXISNAME@units - REQUIRED, but undefined unit category +DEBUG - NXdata.nxdl.xml:/AXISNAME@units - [NX_CHAR] +DEBUG - Dataset referenced as NXdata AXIS #1 +DEBUG - documentation (NXdata.nxdl.xml:/AXISNAME/units): +DEBUG - + Unit in which the coordinate values are expressed. + See the section :ref:`Design-Units` for more information. + DEBUG - ===== FIELD (//entry/definition): DEBUG - value: NXarpes DEBUG - classpath: ['NXentry', 'NX_CHAR'] @@ -421,26 +515,24 @@ DEBUG - <> DEBUG - enumeration (NXarpes.nxdl.xml:/ENTRY/definition): DEBUG - -> NXarpes DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/definition): -DEBUG - - Official NeXus NXDL schema to which this file conforms. - +DEBUG - Official NeXus NXDL schema to which this file conforms. DEBUG - documentation (NXentry.nxdl.xml:/definition): DEBUG - - (alternate use: see same field in :ref:`NXsubentry` for preferred) - - Official NeXus NXDL schema to which this entry conforms which must be - the name of the NXDL file (case sensitive without the file extension) - that the NXDL schema is defined in. - - For example the ``definition`` field for a file that conformed to the - *NXarpes.nxdl.xml* definition must contain the string **NXarpes**. - - This field is provided so that :ref:`NXentry` can be the overlay position - in a NeXus data file for an application definition and its - set of groups, fields, and attributes. - - *It is advised* to use :ref:`NXsubentry`, instead, as the overlay position. - + (alternate use: see same field in :ref:`NXsubentry` for preferred) + + Official NeXus NXDL schema to which this entry conforms which must be + the name of the NXDL file (case sensitive without the file extension) + that the NXDL schema is defined in. + + For example the ``definition`` field for a file that conformed to the + *NXarpes.nxdl.xml* definition must contain the string **NXarpes**. + + This field is provided so that :ref:`NXentry` can be the overlay position + in a NeXus data file for an application definition and its + set of groups, fields, and attributes. + + *It is advised* to use :ref:`NXsubentry`, instead, as the overlay position. + DEBUG - ===== FIELD (//entry/duration): DEBUG - value: 7200 DEBUG - classpath: ['NXentry', 'NX_INT'] @@ -448,9 +540,7 @@ DEBUG - classes: NXentry.nxdl.xml:/duration DEBUG - <> DEBUG - documentation (NXentry.nxdl.xml:/duration): -DEBUG - - Duration of measurement - +DEBUG - Duration of measurement DEBUG - ===== ATTRS (//entry/duration@units) DEBUG - value: s DEBUG - classpath: ['NXentry', 'NX_INT'] @@ -464,25 +554,36 @@ DEBUG - classes: NXentry.nxdl.xml:/end_time DEBUG - <> DEBUG - documentation (NXentry.nxdl.xml:/end_time): -DEBUG - - Ending time of measurement - +DEBUG - Ending time of measurement DEBUG - ===== FIELD (//entry/entry_identifier): DEBUG - value: Run 22118 -DEBUG - classpath: ['NXentry'] -DEBUG - NOT IN SCHEMA -DEBUG - +DEBUG - classpath: ['NXentry', 'NX_CHAR'] +DEBUG - classes: +NXentry.nxdl.xml:/entry_identifier +DEBUG - <> +DEBUG - DEPRECATED - Use the field :ref:`identifier_entry ` instead. +DEBUG - documentation (NXentry.nxdl.xml:/entry_identifier): +DEBUG - unique identifier for the measurement, defined by the facility. DEBUG - ===== FIELD (//entry/experiment_identifier): DEBUG - value: F-20170538 -DEBUG - classpath: ['NXentry'] -DEBUG - NOT IN SCHEMA +DEBUG - classpath: ['NXentry', 'NX_CHAR'] +DEBUG - classes: +NXentry.nxdl.xml:/experiment_identifier +DEBUG - <> +DEBUG - DEPRECATED - Use the field :ref:`identifier_experiment ` instead. +DEBUG - documentation (NXentry.nxdl.xml:/experiment_identifier): DEBUG - + Unique identifier for the experiment, + defined by the facility, + possibly linked to the proposals + DEBUG - ===== GROUP (//entry/instrument [NXarpes::/NXentry/NXinstrument]): DEBUG - classpath: ['NXentry', 'NXinstrument'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT NXentry.nxdl.xml:/INSTRUMENT NXinstrument.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT): DEBUG - @@ -490,15 +591,24 @@ DEBUG - documentation (NXentry.nxdl.xml:/INSTRUMENT): DEBUG - DEBUG - documentation (NXinstrument.nxdl.xml:): DEBUG - - Collection of the components of the instrument or beamline. - - Template of instrument descriptions comprising various beamline components. - Each component will also be a NeXus group defined by its distance from the - sample. Negative distances represent beamline components that are before the - sample while positive distances represent components that are after the sample. - This device allows the unique identification of beamline components in a way - that is valid for both reactor and pulsed instrumentation. - + Collection of the components of the instrument or beamline. + + Template of instrument descriptions comprising various beamline components. + Each component will also be a NeXus group defined by its distance from the + sample. Negative distances represent beamline components that are before the + sample while positive distances represent components that are after the sample. + This device allows the unique identification of beamline components in a way + that is valid for both reactor and pulsed instrumentation. + +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument@NX_class) DEBUG - value: NXinstrument DEBUG - classpath: ['NXentry', 'NXinstrument'] @@ -506,6 +616,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT NXentry.nxdl.xml:/INSTRUMENT NXinstrument.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== GROUP (//entry/instrument/analyser [NXarpes::/NXentry/NXinstrument/NXdetector]): @@ -514,6 +625,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser NXinstrument.nxdl.xml:/DETECTOR NXdetector.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser): DEBUG - @@ -521,8 +634,21 @@ DEBUG - documentation (NXinstrument.nxdl.xml:/DETECTOR): DEBUG - DEBUG - documentation (NXdetector.nxdl.xml:): DEBUG - - A detector, detector bank, or multidetector. + A detector, detector bank, or multidetector. + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/analyser@NX_class) DEBUG - value: NXdetector DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] @@ -530,6 +656,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser NXinstrument.nxdl.xml:/DETECTOR NXdetector.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/analyser/acquisition_mode): @@ -553,19 +681,12 @@ DEBUG - -> pulse counting DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/acquisition_mode): DEBUG - DEBUG - documentation (NXdetector.nxdl.xml:/acquisition_mode): -DEBUG - - The acquisition mode of the detector. - +DEBUG - The acquisition mode of the detector. DEBUG - ===== FIELD (//entry/instrument/analyser/amplifier_type): DEBUG - value: MCP -DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_CHAR'] -DEBUG - classes: -NXdetector.nxdl.xml:/amplifier_type -DEBUG - <> -DEBUG - documentation (NXdetector.nxdl.xml:/amplifier_type): +DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] +DEBUG - NOT IN SCHEMA DEBUG - - Type of electron amplifier, MCP, channeltron, etc. - DEBUG - ===== FIELD (//entry/instrument/analyser/angles): DEBUG - value: [-1.96735314 -1.91500657 -1.86266001 -1.81031344 -1.75796688 -1.70562031 ... DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -574,10 +695,10 @@ NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/angles DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/angles): DEBUG - - Angular axis of the analyser data - which dimension the axis applies to is defined - using the normal NXdata methods. - + Angular axis of the analyser data + which dimension the axis applies to is defined + using the normal NXdata methods. + DEBUG - ===== ATTRS (//entry/instrument/analyser/angles@target) DEBUG - value: /entry/instrument/analyser/angles DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -607,29 +728,29 @@ DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/data): DEBUG - DEBUG - documentation (NXdetector.nxdl.xml:/data): DEBUG - - Data values from the detector. The rank and dimension ordering should follow a principle of - slowest to fastest measurement axes and may be explicitly specified in application definitions. - - Mechanical scanning of objects (e.g. sample position/angle, incident beam energy, etc) tends to be - the slowest part of an experiment and so any such scan axes should be allocated to the first dimensions - of the array. Note that in some cases it may be useful to represent a 2D set of scan points as a single - scan-axis in the data array, especially if the scan pattern doesn't fit a rectangular array nicely. - Repetition of an experiment in a time series tends to be used similar to a slow scan axis - and so will often be in the first dimension of the data array. - - The next fastest axes are typically the readout of the detector. A point detector will not add any dimensions - (as it is just a single value per scan point) to the data array, a strip detector will add one dimension, an - imaging detector will add two dimensions (e.g. X, Y axes) and detectors outputting higher dimensional data - will add the corresponding number of dimensions. Note that the detector dimensions don't necessarily have to - be written in order of the actual readout speeds - the slowest to fastest rule principle is only a guide. - - Finally, detectors that operate in a time-of-flight mode, such as a neutron spectrometer or a silicon drift - detector (used for X-ray fluorescence) tend to have their dimension(s) added to the last dimensions in the data array. - - The type of each dimension should should follow the order of scan points, detector pixels, - then time-of-flight (i.e. spectroscopy, spectrometry). The rank and dimension sizes (see symbol list) - shown here are merely illustrative of coordination between related datasets. - + Data values from the detector. The rank and dimension ordering should follow a principle of + slowest to fastest measurement axes and may be explicitly specified in application definitions. + + Mechanical scanning of objects (e.g. sample position/angle, incident beam energy, etc) tends to be + the slowest part of an experiment and so any such scan axes should be allocated to the first dimensions + of the array. Note that in some cases it may be useful to represent a 2D set of scan points as a single + scan-axis in the data array, especially if the scan pattern doesn't fit a rectangular array nicely. + Repetition of an experiment in a time series tends to be used similar to a slow scan axis + and so will often be in the first dimension of the data array. + + The next fastest axes are typically the readout of the detector. A point detector will not add any dimensions + (as it is just a single value per scan point) to the data array, a strip detector will add one dimension, an + imaging detector will add two dimensions (e.g. X, Y axes) and detectors outputting higher dimensional data + will add the corresponding number of dimensions. Note that the detector dimensions don't necessarily have to + be written in order of the actual readout speeds - the slowest to fastest rule principle is only a guide. + + Finally, detectors that operate in a time-of-flight mode, such as a neutron spectrometer or a silicon drift + detector (used for X-ray fluorescence) tend to have their dimension(s) added to the last dimensions in the data array. + + The type of each dimension should should follow the order of scan points, detector pixels, + then time-of-flight (i.e. spectroscopy, spectrometry). The rank and dimension sizes (see symbol list) + shown here are merely illustrative of coordination between related datasets. + DEBUG - ===== ATTRS (//entry/instrument/analyser/data@target) DEBUG - value: /entry/instrument/analyser/data DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -663,14 +784,9 @@ DEBUG - NOT IN SCHEMA DEBUG - DEBUG - ===== FIELD (//entry/instrument/analyser/detector_type): DEBUG - value: DLD -DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_CHAR'] -DEBUG - classes: -NXdetector.nxdl.xml:/detector_type -DEBUG - <> -DEBUG - documentation (NXdetector.nxdl.xml:/detector_type): +DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] +DEBUG - NOT IN SCHEMA DEBUG - - Description of the detector type, DLD, Phosphor+CCD, CMOS. - DEBUG - ===== FIELD (//entry/instrument/analyser/dispersion_scheme): DEBUG - value: Time of flight DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] @@ -684,10 +800,10 @@ NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/energies DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/energies): DEBUG - - Energy axis of the analyser data - which dimension the axis applies to is defined - using the normal NXdata methods. - + Energy axis of the analyser data + which dimension the axis applies to is defined + using the normal NXdata methods. + DEBUG - ===== ATTRS (//entry/instrument/analyser/energies@target) DEBUG - value: /entry/instrument/analyser/energies DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -708,9 +824,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/entrance_slit_setting DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/entrance_slit_setting): -DEBUG - - dial setting of the entrance slit - +DEBUG - dial setting of the entrance slit DEBUG - ===== FIELD (//entry/instrument/analyser/entrance_slit_shape): DEBUG - value: straight DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_CHAR'] @@ -729,9 +843,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/entrance_slit_size DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/entrance_slit_size): -DEBUG - - size of the entrance slit - +DEBUG - size of the entrance slit DEBUG - ===== ATTRS (//entry/instrument/analyser/entrance_slit_size@units) DEBUG - value: um DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -775,9 +887,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/lens_mode DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/lens_mode): -DEBUG - - setting for the electron analyser lens - +DEBUG - setting for the electron analyser lens DEBUG - ===== FIELD (//entry/instrument/analyser/magnification): DEBUG - value: -1.5 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] @@ -790,9 +900,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/pass_energy DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/pass_energy): -DEBUG - - energy of the electrons on the mean path of the analyser - +DEBUG - energy of the electrons on the mean path of the analyser DEBUG - ===== ATTRS (//entry/instrument/analyser/pass_energy@units) DEBUG - value: eV DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -811,9 +919,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/region_origin DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/region_origin): -DEBUG - - origin of rectangular region selected for readout - +DEBUG - origin of rectangular region selected for readout DEBUG - ===== FIELD (//entry/instrument/analyser/region_size): DEBUG - value: [ 80 146] DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_INT'] @@ -821,19 +927,12 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/region_size DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/region_size): -DEBUG - - size of rectangular region selected for readout - +DEBUG - size of rectangular region selected for readout DEBUG - ===== FIELD (//entry/instrument/analyser/sensor_count): DEBUG - value: 4 -DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_INT'] -DEBUG - classes: -NXdetector.nxdl.xml:/sensor_count -DEBUG - <> -DEBUG - documentation (NXdetector.nxdl.xml:/sensor_count): +DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector'] +DEBUG - NOT IN SCHEMA DEBUG - - Number of imaging sensor chips on the detector. - DEBUG - ===== FIELD (//entry/instrument/analyser/sensor_size): DEBUG - value: [ 80 146] DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_INT'] @@ -841,9 +940,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/sensor_size DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/sensor_size): -DEBUG - - number of raw active elements in each dimension - +DEBUG - number of raw active elements in each dimension DEBUG - ===== FIELD (//entry/instrument/analyser/time_per_channel): DEBUG - value: 7200 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -851,9 +948,7 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/time_per_channel DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/analyser/time_per_channel): -DEBUG - - todo: define more clearly - +DEBUG - todo: define more clearly DEBUG - ===== ATTRS (//entry/instrument/analyser/time_per_channel@units) DEBUG - value: s DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXdetector', 'NX_NUMBER'] @@ -875,33 +970,44 @@ DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam'] DEBUG - classes: NXinstrument.nxdl.xml:/BEAM NXbeam.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXinstrument.nxdl.xml:/BEAM): DEBUG - DEBUG - documentation (NXbeam.nxdl.xml:): DEBUG - - Properties of the neutron or X-ray beam at a given location. - - This group is intended to be referenced - by beamline component groups within the :ref:`NXinstrument` group or by the :ref:`NXsample` group. This group is - especially valuable in storing the results of instrument simulations in which it is useful - to specify the beam profile, time distribution etc. at each beamline component. Otherwise, - its most likely use is in the :ref:`NXsample` group in which it defines the results of the neutron - scattering by the sample, e.g., energy transfer, polarizations. Finally, There are cases where the beam is - considered as a beamline component and this group may be defined as a subgroup directly inside - :ref:`NXinstrument`, in which case it is recommended that the position of the beam is specified by an - :ref:`NXtransformations` group, unless the beam is at the origin (which is the sample). - - Note that incident_wavelength and related fields can be a scalar values or arrays, depending on the use case. - To support these use cases, the explicit dimensionality of these fields is not specified, but it can be inferred - by the presense of and shape of accompanying fields, such as incident_wavelength_weights for a polychromatic beam. - + Properties of the neutron or X-ray beam at a given location. + + This group is intended to be referenced + by beamline component groups within the :ref:`NXinstrument` group or by the :ref:`NXsample` group. This group is + especially valuable in storing the results of instrument simulations in which it is useful + to specify the beam profile, time distribution etc. at each beamline component. Otherwise, + its most likely use is in the :ref:`NXsample` group in which it defines the results of the neutron + scattering by the sample, e.g., energy transfer, polarizations. Finally, There are cases where the beam is + considered as a beamline component and this group may be defined as a subgroup directly inside + :ref:`NXinstrument`, in which case it is recommended that the position of the beam is specified by an + :ref:`NXtransformations` group, unless the beam is at the origin (which is the sample). + + Note that ``incident_wavelength``, ``incident_energy``, and related fields can be a scalar values or arrays, depending on the use case. + To support these use cases, the explicit dimensionality of these fields is not specified, but it can be inferred + by the presence of and shape of accompanying fields, such as incident_wavelength_weights for a polychromatic beam. + +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/beam_probe_0@NX_class) DEBUG - value: NXbeam DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam'] DEBUG - classes: NXinstrument.nxdl.xml:/BEAM NXbeam.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/beam_probe_0/distance): @@ -911,9 +1017,7 @@ DEBUG - classes: NXbeam.nxdl.xml:/distance DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/distance): -DEBUG - - Distance from sample. Note, it is recommended to use NXtransformations instead. - +DEBUG - Distance from sample. Note, it is recommended to use NXtransformations instead. DEBUG - ===== ATTRS (//entry/instrument/beam_probe_0/distance@units) DEBUG - value: cm DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam', 'NX_FLOAT'] @@ -953,7 +1057,7 @@ NXbeam.nxdl.xml:/pulse_duration DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/pulse_duration): DEBUG - - FWHM duration of the pulses at the diagnostic point + FWHM duration of the pulses at the given location. DEBUG - ===== ATTRS (//entry/instrument/beam_probe_0/pulse_duration@units) DEBUG - value: fs @@ -986,33 +1090,44 @@ DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam'] DEBUG - classes: NXinstrument.nxdl.xml:/BEAM NXbeam.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXinstrument.nxdl.xml:/BEAM): DEBUG - DEBUG - documentation (NXbeam.nxdl.xml:): DEBUG - - Properties of the neutron or X-ray beam at a given location. - - This group is intended to be referenced - by beamline component groups within the :ref:`NXinstrument` group or by the :ref:`NXsample` group. This group is - especially valuable in storing the results of instrument simulations in which it is useful - to specify the beam profile, time distribution etc. at each beamline component. Otherwise, - its most likely use is in the :ref:`NXsample` group in which it defines the results of the neutron - scattering by the sample, e.g., energy transfer, polarizations. Finally, There are cases where the beam is - considered as a beamline component and this group may be defined as a subgroup directly inside - :ref:`NXinstrument`, in which case it is recommended that the position of the beam is specified by an - :ref:`NXtransformations` group, unless the beam is at the origin (which is the sample). - - Note that incident_wavelength and related fields can be a scalar values or arrays, depending on the use case. - To support these use cases, the explicit dimensionality of these fields is not specified, but it can be inferred - by the presense of and shape of accompanying fields, such as incident_wavelength_weights for a polychromatic beam. - + Properties of the neutron or X-ray beam at a given location. + + This group is intended to be referenced + by beamline component groups within the :ref:`NXinstrument` group or by the :ref:`NXsample` group. This group is + especially valuable in storing the results of instrument simulations in which it is useful + to specify the beam profile, time distribution etc. at each beamline component. Otherwise, + its most likely use is in the :ref:`NXsample` group in which it defines the results of the neutron + scattering by the sample, e.g., energy transfer, polarizations. Finally, There are cases where the beam is + considered as a beamline component and this group may be defined as a subgroup directly inside + :ref:`NXinstrument`, in which case it is recommended that the position of the beam is specified by an + :ref:`NXtransformations` group, unless the beam is at the origin (which is the sample). + + Note that ``incident_wavelength``, ``incident_energy``, and related fields can be a scalar values or arrays, depending on the use case. + To support these use cases, the explicit dimensionality of these fields is not specified, but it can be inferred + by the presence of and shape of accompanying fields, such as incident_wavelength_weights for a polychromatic beam. + +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0@NX_class) DEBUG - value: NXbeam DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam'] DEBUG - classes: NXinstrument.nxdl.xml:/BEAM NXbeam.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/beam_pump_0/average_power): @@ -1023,7 +1138,7 @@ NXbeam.nxdl.xml:/average_power DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/average_power): DEBUG - - Average power at the diagnostic point + Average power at the at the given location. DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0/average_power@units) DEBUG - value: uW @@ -1048,9 +1163,7 @@ DEBUG - classes: NXbeam.nxdl.xml:/distance DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/distance): -DEBUG - - Distance from sample. Note, it is recommended to use NXtransformations instead. - +DEBUG - Distance from sample. Note, it is recommended to use NXtransformations instead. DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0/distance@units) DEBUG - value: cm DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam', 'NX_FLOAT'] @@ -1065,14 +1178,14 @@ NXbeam.nxdl.xml:/fluence DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/fluence): DEBUG - - Incident fluence at the diagnostic point + Incident energy fluence at the given location. DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0/fluence@units) DEBUG - value: mJ/cm^2 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam', 'NX_FLOAT'] DEBUG - classes: NXbeam.nxdl.xml:/fluence -DEBUG - NXbeam.nxdl.xml:/fluence@units [NX_ANY] +DEBUG - NXbeam.nxdl.xml:/fluence@units [mJ/cm^2] DEBUG - ===== FIELD (//entry/instrument/beam_pump_0/photon_energy): DEBUG - value: 1.55 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXbeam'] @@ -1106,7 +1219,7 @@ NXbeam.nxdl.xml:/pulse_duration DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/pulse_duration): DEBUG - - FWHM duration of the pulses at the diagnostic point + FWHM duration of the pulses at the given location. DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0/pulse_duration@units) DEBUG - value: fs @@ -1122,7 +1235,7 @@ NXbeam.nxdl.xml:/pulse_energy DEBUG - <> DEBUG - documentation (NXbeam.nxdl.xml:/pulse_energy): DEBUG - - Energy of a single pulse at the diagnostic point + Energy of a single pulse at the given location. DEBUG - ===== ATTRS (//entry/instrument/beam_pump_0/pulse_energy@units) DEBUG - value: nJ @@ -1165,19 +1278,36 @@ DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXpositioner'] DEBUG - classes: NXinstrument.nxdl.xml:/POSITIONER NXpositioner.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXinstrument.nxdl.xml:/POSITIONER): DEBUG - DEBUG - documentation (NXpositioner.nxdl.xml:): DEBUG - - A generic positioner such as a motor or piezo-electric transducer. + A generic positioner such as a motor or piezo-electric transducer. + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/manipulator@NX_class) DEBUG - value: NXpositioner DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXpositioner'] DEBUG - classes: NXinstrument.nxdl.xml:/POSITIONER NXpositioner.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/manipulator/pos_x1): @@ -1281,6 +1411,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/monochromator NXinstrument.nxdl.xml:/MONOCHROMATOR NXmonochromator.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/monochromator): DEBUG - @@ -1288,18 +1420,32 @@ DEBUG - documentation (NXinstrument.nxdl.xml:/MONOCHROMATOR): DEBUG - DEBUG - documentation (NXmonochromator.nxdl.xml:): DEBUG - - A wavelength defining device. - - This is a base class for everything which - selects a wavelength or energy, be it a - monochromator crystal, a velocity selector, - an undulator or whatever. - - The expected units are: - - * wavelength: angstrom - * energy: eV + A wavelength defining device. + + This is a base class for everything which + selects a wavelength or energy, be it a + monochromator crystal, a velocity selector, + an undulator or whatever. + + The expected units are: + + * wavelength: angstrom + * energy: eV + + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/monochromator@NX_class) DEBUG - value: NXmonochromator DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXmonochromator'] @@ -1307,6 +1453,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/monochromator NXinstrument.nxdl.xml:/MONOCHROMATOR NXmonochromator.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/monochromator/energy): @@ -1331,9 +1479,7 @@ NXmonochromator.nxdl.xml:/energy_error DEBUG - <> DEBUG - DEPRECATED - see https://github.com/nexusformat/definitions/issues/820 DEBUG - documentation (NXmonochromator.nxdl.xml:/energy_error): -DEBUG - - energy standard deviation - +DEBUG - energy standard deviation DEBUG - ===== ATTRS (//entry/instrument/monochromator/energy_error@units) DEBUG - value: eV DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXmonochromator', 'NX_FLOAT'] @@ -1366,15 +1512,15 @@ DEBUG - classes: NXinstrument.nxdl.xml:/name DEBUG - <> DEBUG - documentation (NXinstrument.nxdl.xml:/name): -DEBUG - - Name of instrument - +DEBUG - Name of instrument DEBUG - ===== GROUP (//entry/instrument/source [NXarpes::/NXentry/NXinstrument/NXsource]): DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE NXinstrument.nxdl.xml:/SOURCE NXsource.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE): DEBUG - @@ -1382,11 +1528,24 @@ DEBUG - documentation (NXinstrument.nxdl.xml:/SOURCE): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:): DEBUG - - Radiation source emitting a beam. - - Examples include particle sources (electrons, neutrons, protons) or sources for electromagnetic radiation (photons). - This base class can also be used to describe neutron or x-ray storage ring/facilities. + Radiation source emitting a beam. + + Examples include particle sources (electrons, neutrons, protons) or sources for electromagnetic radiation (photons). + This base class can also be used to describe neutron or x-ray storage ring/facilities. + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/source@NX_class) DEBUG - value: NXsource DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] @@ -1394,6 +1553,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE NXinstrument.nxdl.xml:/SOURCE NXsource.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/source/bunch_distance): @@ -1403,9 +1564,7 @@ DEBUG - classes: NXsource.nxdl.xml:/bunch_distance DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/bunch_distance): -DEBUG - - For storage rings, time between bunches - +DEBUG - For storage rings, time between bunches DEBUG - ===== ATTRS (//entry/instrument/source/bunch_distance@units) DEBUG - value: us DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1419,9 +1578,7 @@ DEBUG - classes: NXsource.nxdl.xml:/bunch_length DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/bunch_length): -DEBUG - - For storage rings, temporal length of the bunch - +DEBUG - For storage rings, temporal length of the bunch DEBUG - ===== ATTRS (//entry/instrument/source/bunch_length@units) DEBUG - value: fs DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1465,9 +1622,7 @@ DEBUG - classes: NXsource.nxdl.xml:/current DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/current): -DEBUG - - Accelerator, X-ray tube, or storage ring current - +DEBUG - Accelerator, X-ray tube, or storage ring current DEBUG - ===== ATTRS (//entry/instrument/source/current@units) DEBUG - value: uA DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1482,10 +1637,10 @@ NXsource.nxdl.xml:/energy DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/energy): DEBUG - - Source energy. Typically, this would be the energy of - the emitted beam. For storage rings, this would be - the particle beam energy. - + Source energy. Typically, this would be the energy of + the emitted beam. For storage rings, this would be + the particle beam energy. + DEBUG - ===== ATTRS (//entry/instrument/source/energy@units) DEBUG - value: MeV DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1499,9 +1654,7 @@ DEBUG - classes: NXsource.nxdl.xml:/frequency DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/frequency): -DEBUG - - Frequency of pulsed source - +DEBUG - Frequency of pulsed source DEBUG - ===== ATTRS (//entry/instrument/source/frequency@units) DEBUG - value: Hz DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1518,21 +1671,22 @@ DEBUG - enumeration (NXsource.nxdl.xml:/mode): DEBUG - -> Single Bunch DEBUG - -> Multi Bunch DEBUG - documentation (NXsource.nxdl.xml:/mode): -DEBUG - - source operating mode - +DEBUG - source operating mode DEBUG - ===== FIELD (//entry/instrument/source/name): DEBUG - value: FLASH DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_CHAR'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/name NXsource.nxdl.xml:/name +NXcomponent.nxdl.xml:/name DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/name): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/name): +DEBUG - Name of source +DEBUG - documentation (NXcomponent.nxdl.xml:/name): DEBUG - - Name of source + Name of the component. DEBUG - ===== FIELD (//entry/instrument/source/number_of_bunches): DEBUG - value: 500 @@ -1541,9 +1695,7 @@ DEBUG - classes: NXsource.nxdl.xml:/number_of_bunches DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/number_of_bunches): -DEBUG - - For storage rings, the number of bunches in use. - +DEBUG - For storage rings, the number of bunches in use. DEBUG - ===== FIELD (//entry/instrument/source/number_of_bursts): DEBUG - value: 1 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] @@ -1571,9 +1723,7 @@ DEBUG - -> proton DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/probe): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/probe): -DEBUG - - type of radiation probe (pick one from the enumerated list and spell exactly) - +DEBUG - type of radiation probe (pick one from the enumerated list and spell exactly) DEBUG - ===== FIELD (//entry/instrument/source/top_up): DEBUG - value: True DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_BOOLEAN'] @@ -1581,9 +1731,7 @@ DEBUG - classes: NXsource.nxdl.xml:/top_up DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/top_up): -DEBUG - - Is the synchrotron operating in top_up mode? - +DEBUG - Is the synchrotron operating in top_up mode? DEBUG - ===== FIELD (//entry/instrument/source/type): DEBUG - value: Free Electron Laser DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_CHAR'] @@ -1606,27 +1754,26 @@ DEBUG - -> Ion Source DEBUG - -> UV Plasma Source DEBUG - -> Metal Jet X-ray DEBUG - -> Laser -DEBUG - -> Dye-Laser +DEBUG - -> Dye Laser DEBUG - -> Broadband Tunable Light Source -DEBUG - -> Halogen lamp +DEBUG - -> Halogen Lamp DEBUG - -> LED -DEBUG - -> Mercury Cadmium Telluride +DEBUG - -> Mercury Cadmium Telluride Lamp DEBUG - -> Deuterium Lamp DEBUG - -> Xenon Lamp DEBUG - -> Globar -DEBUG - -> other DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/type): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/type): -DEBUG - - type of radiation source (pick one from the enumerated list and spell exactly) - +DEBUG - type of radiation source (pick one from the enumerated list and spell exactly) DEBUG - ===== GROUP (//entry/instrument/source_pump [NXarpes::/NXentry/NXinstrument/NXsource]): DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE NXinstrument.nxdl.xml:/SOURCE NXsource.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE): DEBUG - @@ -1634,11 +1781,24 @@ DEBUG - documentation (NXinstrument.nxdl.xml:/SOURCE): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:): DEBUG - - Radiation source emitting a beam. - - Examples include particle sources (electrons, neutrons, protons) or sources for electromagnetic radiation (photons). - This base class can also be used to describe neutron or x-ray storage ring/facilities. + Radiation source emitting a beam. + + Examples include particle sources (electrons, neutrons, protons) or sources for electromagnetic radiation (photons). + This base class can also be used to describe neutron or x-ray storage ring/facilities. + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/instrument/source_pump@NX_class) DEBUG - value: NXsource DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] @@ -1646,6 +1806,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE NXinstrument.nxdl.xml:/SOURCE NXsource.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/instrument/source_pump/bunch_distance): @@ -1655,9 +1817,7 @@ DEBUG - classes: NXsource.nxdl.xml:/bunch_distance DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/bunch_distance): -DEBUG - - For storage rings, time between bunches - +DEBUG - For storage rings, time between bunches DEBUG - ===== ATTRS (//entry/instrument/source_pump/bunch_distance@units) DEBUG - value: us DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1671,9 +1831,7 @@ DEBUG - classes: NXsource.nxdl.xml:/bunch_length DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/bunch_length): -DEBUG - - For storage rings, temporal length of the bunch - +DEBUG - For storage rings, temporal length of the bunch DEBUG - ===== ATTRS (//entry/instrument/source_pump/bunch_length@units) DEBUG - value: fs DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1707,9 +1865,7 @@ DEBUG - classes: NXsource.nxdl.xml:/frequency DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/frequency): -DEBUG - - Frequency of pulsed source - +DEBUG - Frequency of pulsed source DEBUG - ===== ATTRS (//entry/instrument/source_pump/frequency@units) DEBUG - value: Hz DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_FLOAT'] @@ -1726,21 +1882,22 @@ DEBUG - enumeration (NXsource.nxdl.xml:/mode): DEBUG - -> Single Bunch DEBUG - -> Multi Bunch DEBUG - documentation (NXsource.nxdl.xml:/mode): -DEBUG - - source operating mode - +DEBUG - source operating mode DEBUG - ===== FIELD (//entry/instrument/source_pump/name): DEBUG - value: User Laser @ FLASH DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource', 'NX_CHAR'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/name NXsource.nxdl.xml:/name +NXcomponent.nxdl.xml:/name DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/name): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/name): +DEBUG - Name of source +DEBUG - documentation (NXcomponent.nxdl.xml:/name): DEBUG - - Name of source + Name of the component. DEBUG - ===== FIELD (//entry/instrument/source_pump/number_of_bunches): DEBUG - value: 400 @@ -1749,9 +1906,7 @@ DEBUG - classes: NXsource.nxdl.xml:/number_of_bunches DEBUG - <> DEBUG - documentation (NXsource.nxdl.xml:/number_of_bunches): -DEBUG - - For storage rings, the number of bunches in use. - +DEBUG - For storage rings, the number of bunches in use. DEBUG - ===== FIELD (//entry/instrument/source_pump/number_of_bursts): DEBUG - value: 1 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] @@ -1779,9 +1934,7 @@ DEBUG - -> proton DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/probe): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/probe): -DEBUG - - type of radiation probe (pick one from the enumerated list and spell exactly) - +DEBUG - type of radiation probe (pick one from the enumerated list and spell exactly) DEBUG - ===== FIELD (//entry/instrument/source_pump/rms_jitter): DEBUG - value: 204.68816194453154 DEBUG - classpath: ['NXentry', 'NXinstrument', 'NXsource'] @@ -1814,21 +1967,18 @@ DEBUG - -> Ion Source DEBUG - -> UV Plasma Source DEBUG - -> Metal Jet X-ray DEBUG - -> Laser -DEBUG - -> Dye-Laser +DEBUG - -> Dye Laser DEBUG - -> Broadband Tunable Light Source -DEBUG - -> Halogen lamp +DEBUG - -> Halogen Lamp DEBUG - -> LED -DEBUG - -> Mercury Cadmium Telluride +DEBUG - -> Mercury Cadmium Telluride Lamp DEBUG - -> Deuterium Lamp DEBUG - -> Xenon Lamp DEBUG - -> Globar -DEBUG - -> other DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/INSTRUMENT/SOURCE/type): DEBUG - DEBUG - documentation (NXsource.nxdl.xml:/type): -DEBUG - - type of radiation source (pick one from the enumerated list and spell exactly) - +DEBUG - type of radiation source (pick one from the enumerated list and spell exactly) DEBUG - ===== FIELD (//entry/instrument/spatial_resolution): DEBUG - value: 500 DEBUG - classpath: ['NXentry', 'NXinstrument'] @@ -1856,15 +2006,15 @@ DEBUG - classes: NXentry.nxdl.xml:/run_cycle DEBUG - <> DEBUG - documentation (NXentry.nxdl.xml:/run_cycle): -DEBUG - - Such as "2007-3". Some user facilities organize their beam time into run cycles. - +DEBUG - Such as "2007-3". Some user facilities organize their beam time into run cycles. DEBUG - ===== GROUP (//entry/sample [NXarpes::/NXentry/NXsample]): DEBUG - classpath: ['NXentry', 'NXsample'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/SAMPLE NXentry.nxdl.xml:/SAMPLE NXsample.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/SAMPLE): DEBUG - @@ -1872,12 +2022,25 @@ DEBUG - documentation (NXentry.nxdl.xml:/SAMPLE): DEBUG - DEBUG - documentation (NXsample.nxdl.xml:): DEBUG - - Any information on the sample. - - This could include scanned variables that - are associated with one of the data dimensions, e.g. the magnetic field, or - logged data, e.g. monitored temperature vs elapsed time. + Any information on the sample. + + This could include scanned variables that + are associated with one of the data dimensions, e.g. the magnetic field, or + logged data, e.g. monitored temperature vs elapsed time. + +DEBUG - documentation (NXcomponent.nxdl.xml:): +DEBUG - + Base class for components of an instrument - real ones or simulated ones. +DEBUG - documentation (NXobject.nxdl.xml:): +DEBUG - + This is the base object of NeXus. The groups and fields contained + within this file are allowed to be present in any derived base class. + + If nameType="partial", the placeholders (e.g., FIELDNAME or GROUPNAME) + can be replaced by the name of any object (field or group, + respectively) that exists within the same group. + DEBUG - ===== ATTRS (//entry/sample@NX_class) DEBUG - value: NXsample DEBUG - classpath: ['NXentry', 'NXsample'] @@ -1885,6 +2048,8 @@ DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/SAMPLE NXentry.nxdl.xml:/SAMPLE NXsample.nxdl.xml: +NXcomponent.nxdl.xml: +NXobject.nxdl.xml: DEBUG - @NX_class [NX_CHAR] DEBUG - DEBUG - ===== FIELD (//entry/sample/bias): @@ -1928,14 +2093,15 @@ DEBUG - classpath: ['NXentry', 'NXsample', 'NX_CHAR'] DEBUG - classes: NXarpes.nxdl.xml:/ENTRY/SAMPLE/name NXsample.nxdl.xml:/name +NXcomponent.nxdl.xml:/name DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/SAMPLE/name): -DEBUG - - Descriptive name of sample - +DEBUG - Descriptive name of sample DEBUG - documentation (NXsample.nxdl.xml:/name): +DEBUG - Descriptive name of sample +DEBUG - documentation (NXcomponent.nxdl.xml:/name): DEBUG - - Descriptive name of sample + Name of the component. DEBUG - ===== FIELD (//entry/sample/preparation_method): DEBUG - value: in-vacuum cleave @@ -1949,9 +2115,7 @@ DEBUG - classes: NXsample.nxdl.xml:/pressure DEBUG - <> DEBUG - documentation (NXsample.nxdl.xml:/pressure): -DEBUG - - Applied pressure - +DEBUG - Applied pressure DEBUG - ===== ATTRS (//entry/sample/pressure@units) DEBUG - value: mbar DEBUG - classpath: ['NXentry', 'NXsample', 'NX_FLOAT'] @@ -2001,9 +2165,7 @@ DEBUG - classes: NXsample.nxdl.xml:/thickness DEBUG - <> DEBUG - documentation (NXsample.nxdl.xml:/thickness): -DEBUG - - sample thickness - +DEBUG - sample thickness DEBUG - ===== ATTRS (//entry/sample/thickness@units) DEBUG - value: mm DEBUG - classpath: ['NXentry', 'NXsample', 'NX_FLOAT'] @@ -2025,9 +2187,7 @@ DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/start_time): DEBUG - DEBUG - documentation (NXentry.nxdl.xml:/start_time): -DEBUG - - Starting time of measurement - +DEBUG - Starting time of measurement DEBUG - ===== FIELD (//entry/title): DEBUG - value: Excited-state dynamics of WSe2 in the Valence Band and Core-Levels DEBUG - classpath: ['NXentry', 'NX_CHAR'] @@ -2038,9 +2198,7 @@ DEBUG - <> DEBUG - documentation (NXarpes.nxdl.xml:/ENTRY/title): DEBUG - DEBUG - documentation (NXentry.nxdl.xml:/title): -DEBUG - - Extended title for entry - +DEBUG - Extended title for entry DEBUG - ======================== DEBUG - === Default Plotable === DEBUG - ======================== diff --git a/tests/dataconverter/test_helpers.py b/tests/dataconverter/test_helpers.py index b01ba9e3c..883684012 100644 --- a/tests/dataconverter/test_helpers.py +++ b/tests/dataconverter/test_helpers.py @@ -175,8 +175,8 @@ def test_writing_of_root_attributes(caplog): assert "/@file_time" in keys_added assert "/@file_update_time" in keys_added assert "/@NeXus_repository" in keys_added - assert "/@NeXus_version" in keys_added - assert "/@HDF5_version" in keys_added + assert "/@NeXus_release" in keys_added + assert "/@HDF5_Version" in keys_added assert "/@h5py_version" in keys_added assert "/ENTRY[entry]/definition" in keys_added assert "/ENTRY[entry]/definition/@version" in keys_added diff --git a/tests/dataconverter/test_validation.py b/tests/dataconverter/test_validation.py index 6f32d64c8..80cbbb9b1 100644 --- a/tests/dataconverter/test_validation.py +++ b/tests/dataconverter/test_validation.py @@ -84,6 +84,40 @@ def listify_template(data_dict: Template): TEMPLATE = Template() +TEMPLATE["required"]["/ENTRY[my_entry]/definition"] = "NXtest" # pylint: disable=E1126 +TEMPLATE["required"]["/ENTRY[my_entry]/definition/@version"] = "2.4.6" # pylint: disable=E1126 +TEMPLATE["required"]["/ENTRY[my_entry]/program_name"] = "Testing program" # pylint: disable=E1126 + +TEMPLATE["required"]["/ENTRY[my_entry]/OPTIONAL_group[my_group]/required_field"] = 1 +TEMPLATE["optional"]["/ENTRY[my_entry]/OPTIONAL_group[my_group]/optional_field"] = 1 + +TEMPLATE["required"][ + "/ENTRY[my_entry]/specified_group_with_no_name_type/specified_field_with_no_name_type" +] = 1.0 +TEMPLATE["required"][ + "/ENTRY[my_entry]/specified_group_with_no_name_type/specified_field_with_no_name_type/@specified_attr_in_field_with_no_name_type" +] = "data" +TEMPLATE["required"][ + "/ENTRY[my_entry]/specified_group_with_no_name_type/@specified_attr_with_no_name_type" +] = "attr" + +TEMPLATE["required"]["/ENTRY[my_entry]/specified_group/specified_field"] = 1.0 +TEMPLATE["required"][ + "/ENTRY[my_entry]/specified_group/specified_field/@specified_attr_in_field" +] = "attr" +TEMPLATE["required"]["/ENTRY[my_entry]/specified_group/@specified_attr"] = "attr" + + +TEMPLATE["required"][ + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/any_fieldFIELD[any_fieldFIELD]" +] = 1.0 +TEMPLATE["required"][ + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/any_fieldFIELD[any_fieldFIELD]/@any_attrATTR_in_field[@any_attrATTR_in_field]" +] = "attr" +TEMPLATE["required"][ + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/@any_attrATTR[@any_attrATTR]" +] = "attr" + TEMPLATE["optional"][ "/ENTRY[my_entry]/NXODD_name[nxodd_name]/anamethatRENAMES[anamethatichangetothis]" ] = 2 @@ -94,16 +128,15 @@ def listify_template(data_dict: Template): TEMPLATE["optional"][ "/ENTRY[my_entry]/NXODD_name[nxodd_name]/DATA[float_value_no_attr]" ] = (2.0,) -TEMPLATE["optional"]["/ENTRY[my_entry]/optional_parent/required_child"] = 1 # pylint: disable=E1126 -TEMPLATE["optional"]["/ENTRY[my_entry]/optional_parent/optional_child"] = 1 # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/bool_value"] = True # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/bool_value/@units"] = "" -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/int_value"] = 2 # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/int_value/@units"] = "eV" # pylint: disable=E1126 TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/number_value"] = 2 TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/number_value/@units"] = ( "eV" ) +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/bool_value"] = True # pylint: disable=E1126 +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/bool_value/@units"] = "" +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/int_value"] = 2 # pylint: disable=E1126 +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/int_value/@units"] = "eV" # pylint: disable=E1126 + TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/posint_value"] = np.array( [1, 2, 3], # pylint: disable=E1126 dtype=np.int8, @@ -118,8 +151,15 @@ def listify_template(data_dict: Template): TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/@group_attribute"] = ( "data" # pylint: disable=E1126 ) +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/date_value"] = ( + "2022-01-22T12:14:12.05018+00:00" # pylint: disable=E1126 +) +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/date_value/@units"] = "" +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/type"] = "2nd type" # pylint: disable=E1126 +TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array"] = [0, 1, 2] TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/@signal"] = "data" TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/DATA[data]"] = 1 # pylint: disable=E1126 + TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_two_name]/bool_value"] = True # pylint: disable=E1126 TEMPLATE["required"][ "/ENTRY[my_entry]/NXODD_name[nxodd_two_name]/bool_value/@units" @@ -163,23 +203,16 @@ def listify_template(data_dict: Template): ) TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_two_name]/@signal"] = "data" TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_two_name]/DATA[data]"] = 1 # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/OPTIONAL_group[my_group]/required_field"] = 1 # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/definition"] = "NXtest" # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/definition/@version"] = "2.4.6" # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/program_name"] = "Testing program" # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/type"] = "2nd type" # pylint: disable=E1126 -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array"] = [0, 1, 2] -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/date_value"] = ( - "2022-01-22T12:14:12.05018+00:00" # pylint: disable=E1126 -) -TEMPLATE["required"]["/ENTRY[my_entry]/NXODD_name[nxodd_name]/date_value/@units"] = "" -TEMPLATE["optional"]["/ENTRY[my_entry]/OPTIONAL_group[my_group]/optional_field"] = 1 + TEMPLATE["optional"]["/ENTRY[my_entry]/required_group/description"] = ( "An example description" ) TEMPLATE["optional"]["/ENTRY[my_entry]/required_group2/description"] = ( "An example description" ) + +TEMPLATE["required"]["/ENTRY[my_entry]/optional_parent/required_child"] = 1 # pylint: disable=E1126 +TEMPLATE["optional"]["/ENTRY[my_entry]/optional_parent/optional_child"] = 1 # pylint: disable=E1126 TEMPLATE["required"][ "/ENTRY[my_entry]/optional_parent/req_group_in_opt_group/DATA[data]" ] = 1 @@ -201,6 +234,35 @@ def listify_template(data_dict: Template): @pytest.mark.parametrize( "data_dict,error_messages", [ + pytest.param( + alter_dict( + alter_dict( + alter_dict( + remove_from_dict( + remove_from_dict( + remove_from_dict( + TEMPLATE, + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/any_fieldFIELD[any_fieldFIELD]", + "required", + ), + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/any_fieldFIELD[any_fieldFIELD]/@any_attrATTR_in_field[@any_attrATTR_in_field]", + "required", + ), + "/ENTRY[my_entry]/any_groupGROUP[any_groupGROUP]/@any_attrATTR[@any_attrATTR]", + "required", + ), + "/ENTRY[my_entry]/any_groupGROUP[some_group_name]/any_fieldFIELD[some_field_name]", + 1.0, + ), + "/ENTRY[my_entry]/any_groupGROUP[some_group_name]/any_fieldFIELD[some_field_name]/@any_attrATTR_in_field[@some_attr_name]", + "new attr", + ), + "/ENTRY[my_entry]/any_groupGROUP[some_group_name]/@any_attrATTR[@some_attr_name]", + "new attr", + ), + [], + id="name-type-any", + ), pytest.param( alter_dict( TEMPLATE, @@ -598,13 +660,25 @@ def listify_template(data_dict: Template): [ "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type should " "be one of the following" - ": ['1st type', '2nd type', '3rd type', '4th type']" + ": ['1st type', '2nd type', '3rd type', '4th type']." ], id="wrong-enum-choice", ), + pytest.param( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/NXODD_name[nxodd_name]/type2", + "a very different type", + ), + [ + "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type2 does not match with the " + "enumerated items from the open enumeration: ['1st type open', '2nd type open']." + ], + id="open-enum-with-new-item", + ), pytest.param( set_to_none_in_dict( - TEMPLATE, "/ENTRY[my_entry]/optional_parent/required_child", "optional" + TEMPLATE, "/ENTRY[my_entry]/optional_parent/required_child", "required" ), [ "The data entry corresponding to /ENTRY[my_entry]/optional_parent/" @@ -645,6 +719,40 @@ def listify_template(data_dict: Template): [], id="no-child-provided-optional-parent", ), + pytest.param( + alter_dict( + remove_from_dict( + TEMPLATE, + "/ENTRY[my_entry]/optional_parent/required_child", + "required", + ), + "/ENTRY[my_entry]/optional_parent/AXISNAME[required_child]", + 1, + ), + # ToDo: should not raise a warning if sibling inheritance works + [ + "The data entry corresponding to /ENTRY[my_entry]/optional_parent/" + "required_child is required and hasn't been supplied by the reader." + ], + id="concept-name-given-for-nonvariadic-field", + ), + pytest.param( + alter_dict( + remove_from_dict( + TEMPLATE, + "/ENTRY[my_entry]/optional_parent/optional_child", + "optional", + ), + "/ENTRY[my_entry]/optional_parent/AXISNAME[optional_child]", + "test value", + ), + [ + "The value at /ENTRY[my_entry]/optional_parent/AXISNAME[optional_child] should be " + "one of the following Python types: (, ), as " + "defined in the NXDL as NX_INT." + ], + id="concept-name-given-for-nonvariadic-field-wrong-type", + ), pytest.param(TEMPLATE, "", id="valid-data-dict"), pytest.param( remove_from_dict(TEMPLATE, "/ENTRY[my_entry]/required_group/description"), @@ -695,7 +803,7 @@ def listify_template(data_dict: Template): ), [ "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array should be one of the following Python types: (, ), as defined in the NXDL as NX_INT.", - "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array should be one of the following: [[0, 1, 2], [2, 3, 4]]", + "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array should be one of the following: [[0, 1, 2], [2, 3, 4]].", ], id="wrong-type-array-in-attribute", ), @@ -704,7 +812,7 @@ def listify_template(data_dict: Template): TEMPLATE, "/ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array", [1, 2] ), [ - "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array should be one of the following: [[0, 1, 2], [2, 3, 4]]" + "The value at /ENTRY[my_entry]/NXODD_name[nxodd_name]/type/@array should be one of the following: [[0, 1, 2], [2, 3, 4]]." ], id="wrong-value-array-in-attribute", ), @@ -861,6 +969,18 @@ def listify_template(data_dict: Template): ], id="baseclass-attribute-missing-field", ), + pytest.param( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/INSTRUMENT[my_instrument]/SOURCE[my_source]/target_material", + "Cu", + ), + [ + "The value at /ENTRY[my_entry]/INSTRUMENT[my_instrument]/SOURCE[my_source]/target_material " + "should be one of the following: ['Ta', 'W', 'depleted_U', 'enriched_U', 'Hg', 'Pb', 'C']." + ], + id="baseclass-wrong-enum", + ), pytest.param( alter_dict( TEMPLATE, @@ -868,14 +988,13 @@ def listify_template(data_dict: Template): "Wrong source type", ), [ - "The value at /ENTRY[my_entry]/INSTRUMENT[my_instrument]/SOURCE[my_source]/type " - "should be one of the following: ['Spallation Neutron Source', 'Pulsed Reactor Neutron Source', " - "'Reactor Neutron Source', 'Synchrotron X-ray Source', 'Pulsed Muon Source', 'Rotating Anode X-ray', " - "'Fixed Tube X-ray', 'UV Laser', 'Free-Electron Laser', 'Optical Laser', 'Ion Source', 'UV Plasma Source', " - "'Metal Jet X-ray', 'Laser', 'Dye-Laser', 'Broadband Tunable Light Source', 'Halogen lamp', 'LED', " - "'Mercury Cadmium Telluride', 'Deuterium Lamp', 'Xenon Lamp', 'Globar', 'other']" + "The value at /ENTRY[my_entry]/INSTRUMENT[my_instrument]/SOURCE[my_source]/type does not match with the enumerated " + "items from the open enumeration: ['Spallation Neutron Source', 'Pulsed Reactor Neutron Source', 'Reactor Neutron Source', " + "'Synchrotron X-ray Source', 'Pulsed Muon Source', 'Rotating Anode X-ray', 'Fixed Tube X-ray', 'UV Laser', 'Free-Electron Laser', " + "'Optical Laser', 'Ion Source', 'UV Plasma Source', 'Metal Jet X-ray', 'Laser', 'Dye Laser', 'Broadband Tunable Light Source', " + "'Halogen Lamp', 'LED', 'Mercury Cadmium Telluride Lamp', 'Deuterium Lamp', 'Xenon Lamp', 'Globar']." ], - id="baseclass-wrong-enum", + id="baseclass-open-enum-with-new-item", ), pytest.param( alter_dict( @@ -927,6 +1046,103 @@ def listify_template(data_dict: Template): ], id="baseclass-field-with-illegal-unit", ), + pytest.param( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/identified_calibration/identifier_1", + "123", + ), + [], + id="specified-identifier-with-type", + ), + # ToDo: reactivate if sibling inheritance works properly + # pytest.param( + # alter_dict( + # alter_dict( + # TEMPLATE, + # "/ENTRY[my_entry]/identified_calibration/identifier_1", + # "123", + # ), + # "/ENTRY[my_entry]/identified_calibration/identifier_1/@type", + # "ORCID", + # ), + # [], + # id="specified-identifier-with-type", + # ), + pytest.param( + alter_dict( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/identifierNAME[identifier_id]", + "123", + ), + "/ENTRY[my_entry]/identifierNAME[identifier_id]/@type", + "ORCID", + ), + [], + id="name-fitted-identifier-with-type", + ), + pytest.param( + alter_dict( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/identifierNAME[identifier_id]", + "123", + ), + "/ENTRY[my_entry]/identifierNAME[identifier_id]/@type", + "ORCID", + ), + [], + id="name-fitted-identifier-with-type", + ), + # This can be re-used later when we have proper unit checking + pytest.param( + alter_dict( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/INSTRUMENT[my_instrument]/MONOCHROMATOR[monochromator]/energy_dispersion", + 0.5, + ), + "/ENTRY[my_entry]/INSTRUMENT[my_instrument]/MONOCHROMATOR[monochromator]/energy_dispersion/@units", + "J/mm", + ), + [], + id="baseclass-unit-example", + ), + pytest.param( + alter_dict( + alter_dict( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/COLLECTION[collection]/some_field", + 0.5, + ), + "/ENTRY[my_entry]/COLLECTION[collection]/DATA[data]/some_field", + 0.5, + ), + "/ENTRY[my_entry]/COLLECTION[collection]/DATA[data]/some_field/@units", + "mm", + ), + [], + id="variadic-nxcollection", + ), + pytest.param( + alter_dict( + alter_dict( + alter_dict( + TEMPLATE, + "/ENTRY[my_entry]/named_collection/some_field", + 0.5, + ), + "/ENTRY[my_entry]/named_collection/DATA[data]/some_field", + 0.5, + ), + "/ENTRY[my_entry]/named_collection/DATA[data]/some_field/@units", + "mm", + ), + [], + id="nonvariadic-nxcollection", + ), ], ) def test_validate_data_dict(caplog, data_dict, error_messages, request): @@ -943,6 +1159,10 @@ def format_error_message(msg: str) -> str: if request.node.callspec.id in ( "field-with-illegal-unit", "baseclass-field-with-illegal-unit", + "open-enum-with-new-item", + "baseclass-open-enum-with-new-item", + "variadic-nxcollection", + "nonvariadic-nxcollection", ): with caplog.at_level(logging.INFO): assert validate_dict_against("NXtest", data_dict)[0] diff --git a/tests/nexus/test_nexus.py b/tests/nexus/test_nexus.py index 2069f4dd1..4b14b9753 100644 --- a/tests/nexus/test_nexus.py +++ b/tests/nexus/test_nexus.py @@ -213,77 +213,41 @@ def test_get_node_at_nxdl_path(): """Test to verify if we receive the right XML element for a given NXDL path""" local_dir = os.path.abspath(os.path.dirname(__file__)) nxdl_file_path = os.path.join(local_dir, "../../src/pynxtools/data/NXtest.nxdl.xml") + elem = ET.parse(nxdl_file_path).getroot() + node = get_node_at_nxdl_path("/ENTRY/NXODD_name", elem=elem) assert node.attrib["type"] == "NXdata" assert node.attrib["name"] == "NXODD_name" + node = get_node_at_nxdl_path("/ENTRY/NXODD_name/anamethatRENAMES", elem=elem) + assert node.attrib["type"] == "NX_INT" + assert node.attrib["name"] == "anamethatRENAMES" + assert node.attrib["nameType"] == "partial" + assert node.attrib["units"] == "NX_UNITLESS" + node = get_node_at_nxdl_path("/ENTRY/NXODD_name/float_value", elem=elem) assert node.attrib["type"] == "NX_FLOAT" assert node.attrib["name"] == "float_value" + assert not node.attrib.get("nameType") node = get_node_at_nxdl_path("/ENTRY/NXODD_name/AXISNAME/long_name", elem=elem) assert node.attrib["name"] == "long_name" - nxdl_file_path = os.path.join(local_dir, "../data/nexus/NXtest2.nxdl.xml") - elem = ET.parse(nxdl_file_path).getroot() - node = get_node_at_nxdl_path( - "/ENTRY/measurement/EVENT_DATA_EM/USER/affiliation", elem=elem - ) - assert node.attrib["name"] == "affiliation" - - node = get_node_at_nxdl_path("/ENTRY/measurement", elem=elem) - assert node.attrib["type"] == "NXevent_data_em_set" - - node = get_node_at_nxdl_path( - "/ENTRY/measurement/EVENT_DATA_EM/SPECTRUM_SET/stack_3d", elem=elem - ) - assert node.attrib["type"] == "NXdata" - - node = get_node_at_nxdl_path( - "/ENTRY/measurement/EVENT_DATA_EM/SPECTRUM_SET/stack_3d/intensity", elem=elem - ) - assert node.attrib["type"] == "NX_NUMBER" + node = get_node_at_nxdl_path("/ENTRY/NXODD_name/group_attribute", elem=elem) + assert node.attrib["name"] == "group_attribute" - node = get_node_at_nxdl_path( - "/ENTRY/measurement/EVENT_DATA_EM/SPECTRUM_SET/stack_3d/AXISNAME_indices", - elem=elem, - ) - assert node.attrib["name"] == "AXISNAME_indices" - - node = get_node_at_nxdl_path("/ENTRY/COORDINATE_SYSTEM_SET", elem=elem) - assert node.attrib["type"] == "NXcoordinate_system_set" + node = get_node_at_nxdl_path("/ENTRY/optional_parent", elem=elem) + assert node.attrib["name"] == "optional_parent" + assert node.attrib["optional"] == "true" - node = get_node_at_nxdl_path( - "/ENTRY/COORDINATE_SYSTEM_SET/TRANSFORMATIONS", elem=elem - ) - assert node.attrib["type"] == "NXtransformations" + node = get_node_at_nxdl_path("/ENTRY/optional_parent/required_child", elem=elem) + assert node.attrib["name"] == "required_child" + assert node.attrib["type"] == "NX_INT" + assert node.attrib["required"] == "true" - node = get_node_at_nxdl_path( - "/ENTRY/COORDINATE_SYSTEM_SET/TRANSFORMATIONS/AXISNAME", elem=elem - ) - assert node.attrib["type"] == "NX_NUMBER" - - node = get_node_at_nxdl_path( - "/ENTRY/COORDINATE_SYSTEM_SET/TRANSFORMATIONS/AXISNAME/transformation_type", - elem=elem, - ) - assert node.attrib["name"] == "transformation_type" - - nxdl_file_path = os.path.join( - local_dir, - "../../src/pynxtools/definitions/contributed_definitions/NXiv_temp.nxdl.xml", - ) - elem = ET.parse(nxdl_file_path).getroot() - node = get_node_at_nxdl_path( - "/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller", elem=elem - ) - assert node.attrib["name"] == "voltage_controller" - - node = get_node_at_nxdl_path( - "/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller/calibration_time", elem=elem - ) - assert node.attrib["name"] == "calibration_time" + node = get_node_at_nxdl_path("/ENTRY/USER/affiliation", elem=elem) + assert node.attrib["name"] == "affiliation" def test_get_inherited_nodes(): @@ -297,18 +261,18 @@ def test_get_inherited_nodes(): (_, _, elist) = get_inherited_nodes( nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT", elem=elem ) - assert len(elist) == 3 + assert len(elist) == 4 (_, _, elist) = get_inherited_nodes( nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller", elem=elem ) - assert len(elist) == 4 + assert len(elist) == 6 (_, _, elist) = get_inherited_nodes( nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller", nx_name="NXiv_temp", ) - assert len(elist) == 4 + assert len(elist) == 6 def test_c_option(tmp_path): diff --git a/tests/nomad/test_parsing.py b/tests/nomad/test_parsing.py index e050448a7..694517c98 100644 --- a/tests/nomad/test_parsing.py +++ b/tests/nomad/test_parsing.py @@ -57,15 +57,16 @@ def test_nexus_example(): ) # good ENUM - x-ray assert instrument.SOURCE[0].probe__field == "x-ray" - # wrong inherited ENUM - Burst - assert instrument.SOURCE[0].mode__field is None - # wrong inherited ENUM for extended field - 'Free Electron Laser' - assert instrument.SOURCE[0].type__field is None + # wrong inherited ENUM - Burst (accepted for open enum) + assert instrument.SOURCE[0].mode__field == "Burst" + # wrong inherited ENUM for extended field - 'Free Electron Laser' (accepted for open enum) + assert instrument.SOURCE[0].type__field == "Free Electron Laser" data = arpes_obj.ENTRY[0].DATA[0] assert len(data.AXISNAME__field) == 3 # there is still a bug in the variadic name resolution, so skip these - # assert data.delays__field is not None - # assert data.angles__field.check("1/Å") + assert data.delays__field is not None + assert data.angles__field.check("1/Å") + # ToDo: if AXISNAME and DATA can be resolved properly, extend this! # assert data.delays__field.check("fs") # but the following still works assert data.energies__field is not None @@ -76,6 +77,16 @@ def test_nexus_example(): assert (1 * data.AXISNAME__field["angles__field"].unit).check("1/Å") assert (1 * data.AXISNAME__field["delays__field"].unit).check("fs") assert data.___axes == "['angles', 'energies', 'delays']" + # testing attributes + assert ( + data.AXISNAME__field["angles__field"].attributes.get("m_nx_data_path") + == "/entry/data/angles" + ) + assert ( + data.m_get_quantity_attribute("angles__field", "m_nx_data_path") + == "/entry/data/angles" + ) + assert data.m_attributes.get("m_nx_data_path") == "/entry/data" def test_same_name_field_and_group():