From db8d3e372c7d0cafe3c6eaf8962a541b24d2c98c Mon Sep 17 00:00:00 2001 From: Markus Scheidgen <markus.scheidgen@gmail.com> Date: Sun, 15 Sep 2019 20:26:36 +0200 Subject: [PATCH] Added a new proof of concept metainfo library impl. --- nomad/metainfo/metainfo.py | 239 +++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 nomad/metainfo/metainfo.py diff --git a/nomad/metainfo/metainfo.py b/nomad/metainfo/metainfo.py new file mode 100644 index 0000000000..04cef7d48c --- /dev/null +++ b/nomad/metainfo/metainfo.py @@ -0,0 +1,239 @@ +from typing import Dict, List, Any, Union, Type +import json +import ase.data + + +class Units(): + + def __getattribute__(self, name): + return name + + +units = Units() + + +class Definition(): + pass + + +class Property(Definition): + pass + + +class Quantity(Property): + def __init__( + self, + name: str = None, + description: str = None, + parent_section: 'Section' = None, + shape: List[Union[str, int]] = [], + type: type = None, + unit: str = None, + derived: bool = False, + repeats: bool = False, + synonym: str = None): + + self.name = name + self.parent_section = parent_section.m_definition if parent_section is not None else None + self.derived = derived + self.synonym = synonym + + def __get__(self, obj: 'MetainfoObject', type: Type): + if obj is None: + # access on cls not obj + return self + + if self.derived: + derive_method = getattr(obj, 'm_derive_%s' % self.name, None) + + if derive_method is None: + raise KeyError('Derived quantity %s is not implemented' % self.name) + + else: + return derive_method() + + elif self.synonym is not None: + return getattr(obj, self.synonym) + + else: + return obj.m_data.get(self.name, None) + + def __set__(self, obj: 'MetainfoObject', value: Any): + obj.m_data[self.name] = value + + def __delete__(self, obj: 'MetainfoObject'): + del obj.m_data[self.name] + + def __repr__(self): + base = self.name + if self.parent_section is not None: + return '%s.%s' % (str(self.parent_section), base) + else: + return base + + +class Section(Definition): + def __init__( + self, + name: str = None, + parent_section: 'Section' = None, + repeats: bool = False, + extends: 'Section' = None, + adds_to: 'Section' = None): + + self.name = name + self.parent_section = parent_section.m_definition if parent_section is not None else None + self.repeats = repeats + + def __get__(self, obj: 'MetainfoObject', type: Type): + return self + + def __repr__(self): + base = self.name + if self.parent_section is not None: + return '%s.%s' % (str(self.parent_section), base) + else: + return base + + +class Reference(Property): + pass + + +class Enum: + def __init__(self, values: List[Any]): + self.values = values + + +class MetainfoObjectMeta(type): + def __new__(cls, cls_name, bases, dct): + cls.m_definition = dct.get('m_definition', None) + for name, value in dct.items(): + if isinstance(value, Property): + value.name = name + value.parent_section = cls.m_definition + + cls = super().__new__(cls, cls_name, bases, dct) + + if cls.m_definition is not None: + if cls.m_definition.name is None: + cls.m_definition.name = cls_name + + return cls + + +class MetainfoObject(metaclass=MetainfoObjectMeta): + """ + Base class for all + """ + + def __init__(self): + self.m_data = dict(m_defintion=self.m_definition.name) + + def m_create(self, section_definition: Type['MSection'], *args, **kwargs) -> 'MSection': + """ + Creates a sub section of the given section definition. + """ + section_cls = section_definition + definition = section_definition.m_definition + sub_section = section_cls(*args, **kwargs) + if definition.repeats: + self.m_data.setdefault(definition.name, []).append(sub_section) + else: + # TODO test overwrite + self.m_data[definition.name] = sub_section + + return sub_section + + def m_get_definition(self, name): + """ + Returns the definition of the given quantity name. + """ + descriptor = getattr(type(self), name) + if descriptor is None: + raise KeyError + elif not isinstance(descriptor, Property): + raise KeyError + + return descriptor + + def m_to_dict(self) -> Dict[str, Any]: + """ + Returns a JSON serializable dictionary version of this section (and all subsections). + """ + return { + key: value.m_to_dict() if isinstance(value, MetainfoObject) else value + for key, value in self.m_data.items() + } + + def m_to_json(self) -> str: + return json.dumps(self.m_to_dict(), indent=2) + + def m_validate(self) -> bool: + """ + Validates this sections content based on section and quantity definitions. + Can be overwritten to customize the validation. + """ + return True + + def __repr__(self) -> str: + return self.m_to_json() + + +class Run(MetainfoObject): + m_definition = Section() + + +class System(MetainfoObject): + """ + The system is ... + """ + m_definition = Section(parent_section=Run, repeats=True) + + n_atoms = Quantity(type=int, derived=True) + + atom_labels = Quantity(shape=['n_atoms'], type=Enum(ase.data.chemical_symbols)) + """ + Atom labels are ... + """ + + atom_species = Quantity(shape=['n_atoms'], type=int, derived=True) + + atom_positions = Quantity(shape=['n_atoms', 3], type=float, unit=units.m) + + cell = Quantity(shape=[3, 3], type=float, unit=units.m) + lattice_vectors = Quantity(synonym='cell') + + pbc = Quantity(shape=[3], type=bool) + + def m_derive_atom_species(self) -> List[int]: + return [ase.data.atomic_numbers[label] for label in self.atom_labels] + + def m_derive_n_atoms(self) -> int: + return len(self.atom_labels) + + +class VaspSystem(MetainfoObject): + m_definition = Section(adds_to=System) + + vasp_specific = Quantity(type=str) + + +run = Run() +print(run.m_definition) + +system = run.m_create(System) +system.atom_labels = ['H', 'H', 'O'] +system.atom_positions = [[0, 0, 0], [1, 0, 0], [0.5, 0.5, 0]] +system.cell = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] +system.pbc = [False, False, False] + +print(system.atom_species) +print(system.lattice_vectors) +print(system.n_atoms) + +print(system.__class__.m_definition) +print(system.m_definition) +print(system.m_get_definition('atom_labels')) + +print(run) \ No newline at end of file -- GitLab