diff --git a/nomad/datamodel/datamodel.py b/nomad/datamodel/datamodel.py index 0249e890ba0c9b50fc8003d66cc1ae2c166ff9dd..961988022412860c86a9da2c0c4b3c34c0bc62e0 100644 --- a/nomad/datamodel/datamodel.py +++ b/nomad/datamodel/datamodel.py @@ -228,6 +228,21 @@ def derive_authors(entry: 'EntryMetadata') -> List[User]: return authors +class CompatibleSectionDef(MSection): + definition_qualified_name = Quantity( + type=str, + description='The qualified name of the compatible section.', + a_elasticsearch=Elasticsearch(material_entry_type)) + definition_id = Quantity( + type=str, + description='The definition id of the compatible section.', + a_elasticsearch=Elasticsearch(material_entry_type)) + used_directly = Quantity( + type=bool, + description='If the compatible section is directly used as base section.', + a_elasticsearch=Elasticsearch(material_entry_type)) + + class EntryArchiveReference(MSection): m_def = Section(label='ArchiveReference') @@ -643,9 +658,15 @@ class EntryMetadata(MSection): sections = Quantity( type=str, shape=['*'], - description='All sections that are present in this entry.', + description='All sections that are present in this entry. This field is deprecated and will be removed.', a_elasticsearch=Elasticsearch(material_entry_type)) + section_defs = SubSection( + sub_section=CompatibleSectionDef, + repeats=True, + description='All sections that are compatible with the present sections in this entry.', + a_elasticsearch=Elasticsearch(material_entry_type, nested=True)) + entry_references = SubSection( sub_section=EntryArchiveReference, repeats=True, @@ -826,7 +847,7 @@ class EntryMetadata(MSection): if len(entry_references) > 0: for archive_reference_quantity in EntryArchiveReference.m_def.quantities: # pylint: disable=not-an-iterable quantities.add(f'metadata.entry_references.{archive_reference_quantity.name}') - quantities.add('metadata.entry_references') + quantities.add('metadata.entry_references') sections.add(EntryArchiveReference.m_def) if len(quantities) > 0: @@ -834,6 +855,9 @@ class EntryMetadata(MSection): if len(sections) > 0: quantities.add('metadata.sections') + quantities.add('metadata.section_defs') + for compatible_quantity in CompatibleSectionDef.m_def.quantities: + quantities.add(f'metadata.section_defs.{compatible_quantity.name}') self.entry_references.extend(entry_references) self.searchable_quantities.extend(searchable_quantities) @@ -843,6 +867,32 @@ class EntryMetadata(MSection): self.sections.sort() self.n_quantities = n_quantities + def generate_compatible(_s, used_directly: bool): + return CompatibleSectionDef( + definition_qualified_name=_s.qualified_name(), + definition_id=_s.definition_id, + used_directly=used_directly) + + def collect_base_sections(_section, used_directly: bool = False): + for _b in _section.base_sections: + if used_directly: + # always overrides the directly used section definitions + section_defs[_b.qualified_name()] = generate_compatible(_b, used_directly=used_directly) + elif _b.qualified_name() not in section_defs: + # indirect usage may be directly used elsewhere, do not overwrite + section_defs[_b.qualified_name()] = generate_compatible(_b, used_directly=used_directly) + # all the base sections of the base sections are indirectly used + collect_base_sections(_b, used_directly=False) + + section_defs = {} + for section in sections: + section_defs[section.qualified_name()] = generate_compatible(section, used_directly=True) + for extending in section.extending_sections: + section_defs[extending.qualified_name()] = generate_compatible(extending, used_directly=True) + collect_base_sections(section, used_directly=True) + + self.section_defs = sorted(list(section_defs.values()), key=lambda x: x.definition_qualified_name) + class EntryArchive(ArchiveSection): m_def = Section(label='Entry') diff --git a/tests/datamodel/test_datamodel.py b/tests/datamodel/test_datamodel.py index a187770f818186ee162c5d8d6f17b11c98e7ba55..4453066ebbe1dd724d70f5f0588934433a016d24 100644 --- a/tests/datamodel/test_datamodel.py +++ b/tests/datamodel/test_datamodel.py @@ -19,10 +19,11 @@ ''' A generator for random test calculations. ''' - import random from essential_generators import DocumentGenerator +from nomad.datamodel import EntryArchive, EntryMetadata +from nomad.metainfo import MSection, Quantity, SubSection from nomad.parsing.parsers import parser_dict number_of = 20 @@ -30,7 +31,8 @@ number_of = 20 random.seed(0) gen = DocumentGenerator() -users = ['20bb9766-d338-4314-be43-7906042a5086', 'a03af8b6-3aa7-428a-b3b1-4a6317e576b6', '54cb1f64-f84e-4815-9ade-440ce0b5430f'] +users = ['20bb9766-d338-4314-be43-7906042a5086', 'a03af8b6-3aa7-428a-b3b1-4a6317e576b6', + '54cb1f64-f84e-4815-9ade-440ce0b5430f'] basis_sets = ['Numeric AOs', 'Gaussians', '(L)APW+lo', 'Plane waves'] xc_functionals = ['LDA', 'GGA', 'hybrid', 'meta-GGA', 'GW', 'unknown'] crystal_systems = ['triclinic', 'monoclinic', 'orthorombic', 'tetragonal', 'hexagonal', 'cubic']