Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
nomad-FAIR
Commits
30136a3b
Commit
30136a3b
authored
Sep 18, 2019
by
Markus Scheidgen
Browse files
A new iteration of metainfo re-implementation.
parent
9f4afd3c
Changes
5
Hide whitespace changes
Inline
Side-by-side
nomad/metainfo/
bootstrap
.py
→
nomad/metainfo/
api_tryout
.py
View file @
30136a3b
File moved
nomad/metainfo/metainfo.py
View file @
30136a3b
"""
Some playground to try the CONCEPT.md ideas.
"""
# Copyright 2018 Markus Scheidgen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an"AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from
typing
import
Dict
,
List
,
Any
,
Union
,
Type
import
json
import
ase.data
from
typing
import
Type
,
TypeVar
,
Union
,
Tuple
,
Iterable
,
List
,
Any
,
Dict
import
sys
class
Units
():
__module__
=
sys
.
modules
[
__name__
]
MObjectBound
=
TypeVar
(
'MObjectBound'
,
bound
=
'MObject'
)
def
__getattribute__
(
self
,
name
):
return
name
# Reflection
units
=
Units
()
class
MObjectMeta
(
type
):
def
__new__
(
self
,
cls_name
,
bases
,
dct
):
cls
=
super
().
__new__
(
self
,
cls_name
,
bases
,
dct
)
init
=
getattr
(
cls
,
'__init_section_cls__'
)
if
init
is
not
None
:
init
()
return
cls
class
Definition
():
m_definition
:
Any
=
None
pass
Content
=
Tuple
[
MObjectBound
,
Union
[
List
[
MObjectBound
],
MObjectBound
],
str
,
MObjectBound
]
class
Property
(
Definition
):
pass
class
MObject
(
metaclass
=
MObjectMeta
):
class
Quantity
(
Property
):
def
__init__
(
self
,
name
:
str
=
None
,
description
:
str
=
None
,
parent_section
:
'Section'
=
None
,
shape
:
List
[
Union
[
str
,
int
]]
=
[],
type
:
Union
[
'Enum'
,
type
]
=
None
,
unit
:
str
=
None
,
derived
:
bool
=
False
,
repeats
:
bool
=
False
,
synonym
:
str
=
None
):
def
__init__
(
self
,
**
kwargs
):
self
.
m_section
:
'Section'
=
kwargs
.
pop
(
'm_section'
,
getattr
(
self
.
__class__
,
'm_section'
,
None
))
# TODO test with failure instead of None
self
.
m_data
=
dict
(
**
kwargs
)
self
.
name
=
name
self
.
parent_section
=
parent_section
.
m_definition
if
parent_section
is
not
None
else
None
self
.
derived
=
derived
self
.
synonym
=
synonym
@
classmethod
def
__init_section_cls__
(
cls
):
if
not
hasattr
(
__module__
,
'Quantity'
)
or
not
hasattr
(
__module__
,
'Section'
):
# no initialization during bootstrapping, will be done maunally
return
def
__get__
(
self
,
obj
:
'MetainfoObject'
,
type
:
Type
):
if
obj
is
None
:
# access on cls not obj
return
self
m_section
=
getattr
(
cls
,
'm_section'
,
None
)
if
m_section
is
None
:
m_section
=
Section
()
setattr
(
cls
,
'm_section'
,
m_section
)
m_section
.
name
=
cls
.
__name__
if
self
.
derived
:
derive_method
=
getattr
(
obj
,
'm_derive_%s'
%
self
.
name
,
None
)
for
name
,
value
in
cls
.
__dict__
.
items
():
if
isinstance
(
value
,
Quantity
):
value
.
name
=
name
m_section
.
m_add
(
'Quantity'
,
value
)
if
derive_method
is
None
:
raise
KeyError
(
'Derived quantity %s is not implemented'
%
self
.
name
)
def
m_create
(
self
,
section
:
Union
[
'Section'
,
Type
[
MObjectBound
]],
**
kwargs
)
->
MObjectBound
:
"""Creates a subsection and adds it this this section
else
:
return
derive_method
()
Args:
section: The section definition of the subsection. It is either the
definition itself, or the python class representing the section definition.
**kwargs: Are used to initialize the subsection.
elif
self
.
synonym
is
not
None
:
return
getattr
(
obj
,
self
.
synonym
)
Returns
:
The created subsection
Raises:
ValueError: If the given section is not a subsection of this section, or
this given definition is not a section at all.
"""
section_def
:
'Section'
=
None
if
isinstance
(
section
,
type
)
and
hasattr
(
section
,
'm_section'
):
section_def
=
section
.
m_section
elif
isinstance
(
section
,
Section
):
section_def
=
section
else
:
r
eturn
obj
.
m_data
.
get
(
self
.
name
,
N
on
e
)
r
aise
ValueError
(
'Not a section definiti
on
'
)
def
__set__
(
self
,
obj
:
'MetainfoObject'
,
value
:
Any
)
:
obj
.
m_data
[
self
.
name
]
=
value
if
section_def
.
parent
!=
self
.
m_section
:
raise
ValueError
(
'Not a subsection'
)
def
__delete__
(
self
,
obj
:
'MetainfoObject'
):
del
obj
.
m_data
[
self
.
name
]
section_cls
=
section_def
.
section_cls
section_instance
=
section_cls
(
**
kwargs
)
def
__repr__
(
self
):
base
=
self
.
name
if
self
.
parent_section
is
not
None
:
return
'%s.%s'
%
(
str
(
self
.
parent_section
),
base
)
if
section_def
.
repeats
:
self
.
m_data
.
setdefault
(
section_def
.
name
,
[]).
append
(
section_instance
)
else
:
return
bas
e
self
.
m_data
[
section_def
.
name
]
=
section_instanc
e
return
section_instance
class
Section
(
Definition
):
def
__init__
(
self
,
name
:
str
=
None
,
parent_section
=
None
,
repeats
:
bool
=
False
,
extends
=
None
,
adds_to
=
None
):
def
m_set
(
self
,
name
:
str
,
value
:
Any
):
self
.
m_data
[
name
]
=
value
self
.
name
=
name
self
.
parent_section
=
parent_section
.
m_definition
if
parent_section
is
not
None
else
None
self
.
repeats
=
repeats
def
m_add
(
self
,
name
:
str
,
value
:
Any
):
values
=
self
.
m_data
.
setdefault
(
name
,
[])
values
.
append
(
value
)
def
__get__
(
self
,
obj
:
'MetainfoObject'
,
type
:
Type
):
return
self
def
m_get
(
self
,
name
:
str
)
->
Any
:
try
:
return
self
.
m_data
[
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
except
KeyError
:
return
self
.
m_section
.
attributes
[
name
].
default
def
m_delete
(
self
,
name
:
str
):
del
self
.
m_data
[
name
]
class
Reference
(
Property
):
pass
def
m_to_dict
(
self
):
pass
def
m_to_json
(
self
):
pass
class
Enum
:
def
__init__
(
self
,
values
:
List
[
Any
]):
self
.
values
=
values
def
m_all_contents
(
self
)
->
Iterable
[
Content
]:
for
content
in
self
.
m_contents
():
for
sub_content
in
content
[
0
].
m_all_contents
():
yield
sub_content
yield
content
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
def
m_contents
(
self
)
->
Iterable
[
Content
]:
for
name
,
attr
in
self
.
m_data
.
items
():
if
isinstance
(
attr
,
list
):
for
value
in
attr
:
if
isinstance
(
value
,
MObject
):
yield
value
,
attr
,
name
,
self
cls
=
super
().
__new__
(
cls
,
cls_name
,
bases
,
dct
)
elif
isinstance
(
attr
,
MObject
):
yield
value
,
value
,
name
,
self
if
cls
.
m_definition
is
not
None
:
if
cls
.
m_definition
.
name
is
None
:
cls
.
m_definition
.
name
=
cls_name
def
__repr__
(
self
):
m_section_name
=
self
.
m_section
.
name
name
=
''
if
'name'
in
self
.
m_data
:
name
=
self
.
m_data
[
'name'
]
return
'%s:%s'
%
(
name
,
m_section_name
)
return
cls
class
Enum
(
list
):
pass
class
MetainfoObject
(
metaclass
=
MetainfoObjectMeta
):
"""
Base class for all
"""
m_definition
:
Any
=
None
def
__init__
(
self
):
self
.
m_data
=
dict
(
m_defintion
=
self
.
m_definition
.
name
)
# M3
def
m_create
(
self
,
section_definition
:
Any
,
*
args
,
**
kwargs
)
->
Any
:
"""
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
class
Quantity
(
MObject
):
m_section
:
'Section'
=
None
name
:
'Quantity'
=
None
type
:
'Quantity'
=
None
shape
:
'Quantity'
=
None
return
sub_section
__name
=
property
(
lambda
self
:
self
.
m_data
[
'name'
])
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
default
=
property
(
lambda
self
:
None
)
return
descriptor
def
__get__
(
self
,
obj
,
type
=
None
):
return
obj
.
m_get
(
self
.
__name
)
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
__set__
(
self
,
obj
,
value
):
obj
.
m_set
(
self
.
__name
,
value
)
def
m_to_json
(
self
)
->
str
:
return
json
.
dumps
(
self
.
m_to_dict
(),
indent
=
2
)
def
__delete__
(
self
,
obj
)
:
obj
.
m_delete
(
self
.
__name
)
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
Section
(
MObject
):
m_section
:
'Section'
=
None
name
:
'Quantity'
=
None
repeats
:
'Quantity'
=
None
parent
:
'Quantity'
=
None
extends
:
'Quantity'
=
None
__all_instances
:
List
[
'Section'
]
=
[]
class
Run
(
MetainfoObject
):
m_definition
=
Section
(
)
default
=
property
(
lambda
self
:
[]
if
self
.
repeats
else
None
)
section_cls
=
property
(
lambda
self
:
self
.
__class__
)
def
__init__
(
self
,
**
kwargs
):
# The mechanism that produces default values, depends on parent. Without setting
# the parent default manually, an endless recursion will occur.
kwargs
.
setdefault
(
'parent'
,
None
)
class
System
(
MetainfoObject
):
"""
The system is ...
"""
m_definition
=
Section
(
parent_section
=
Run
,
repeats
=
True
)
super
().
__init__
(
**
kwargs
)
Section
.
__all_instances
.
append
(
self
)
n_atoms
=
Quantity
(
type
=
int
,
derived
=
True
)
# TODO cache
@
property
def
attributes
(
self
)
->
Dict
[
str
,
Union
[
'Section'
,
Quantity
]]:
""" All attribute (sub section and quantity) definitions. """
atom_labels
=
Quantity
(
shape
=
[
'n_atoms'
],
type
=
Enum
(
ase
.
data
.
chemical_symbols
))
"""
Atom labels are ...
"""
attributes
:
Dict
[
str
,
Union
[
Section
,
Quantity
]]
=
dict
(
**
self
.
quantities
)
attributes
.
update
(
**
self
.
sub_sections
)
return
attributes
atom_species
=
Quantity
(
shape
=
[
'n_atoms'
],
type
=
int
,
derived
=
True
)
# TODO cache
@
property
def
quantities
(
self
)
->
Dict
[
str
,
Quantity
]:
""" All quantity definition in the given section definition. """
atom_positions
=
Quantity
(
shape
=
[
'n_atoms'
,
3
],
type
=
float
,
unit
=
units
.
m
)
return
{
quantity
.
name
:
quantity
for
quantity
in
self
.
m_data
.
get
(
'Quantity'
,
[])}
cell
=
Quantity
(
shape
=
[
3
,
3
],
type
=
float
,
unit
=
units
.
m
)
lattice_vectors
=
Quantity
(
synonym
=
'cell'
)
# TODO cache
@
property
def
sub_sections
(
self
)
->
Dict
[
str
,
'Section'
]:
""" All sub section definitions for this section definition. """
pbc
=
Quantity
(
shape
=
[
3
],
type
=
bool
)
return
{
sub_section
.
name
:
sub_section
for
sub_section
in
Section
.
__all_instances
if
sub_section
.
parent
==
self
}
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
)
Section
.
m_section
=
Section
(
repeats
=
True
,
name
=
'Section'
)
Section
.
m_section
.
m_section
=
Section
.
m_section
Section
.
name
=
Quantity
(
type
=
str
,
name
=
'name'
)
Section
.
repeats
=
Quantity
(
type
=
bool
,
name
=
'repeats'
)
Section
.
parent
=
Quantity
(
type
=
Section
.
m_section
,
name
=
'parent'
)
Section
.
extends
=
Quantity
(
type
=
Section
.
m_section
,
shape
=
[
'0..*'
],
name
=
'extends'
)
class
VaspSystem
(
MetainfoObject
):
m_definition
=
Section
(
adds_to
=
System
)
Quantity
.
m_section
=
Section
(
repeats
=
True
,
parent
=
Section
.
m_section
,
name
=
'Quantity'
)
Quantity
.
name
=
Quantity
(
type
=
str
,
name
=
'name'
)
Quantity
.
type
=
Quantity
(
type
=
Union
[
type
,
Enum
,
Section
],
name
=
'type'
)
Quantity
.
shape
=
Quantity
(
type
=
Union
[
str
,
int
],
shape
=
[
'0..*'
],
name
=
'shape'
)
vasp_specific
=
Quantity
(
type
=
str
)
class
Package
(
MObject
):
m_section
=
Section
()
name
=
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
]
Section
.
m_section
.
parent
=
Package
.
m_section
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'
))
class
Definition
(
MObject
):
m_section
=
Section
(
extends
=
[
Section
.
m_section
,
Quantity
.
m_section
,
Package
.
m_section
])
print
(
run
)
description
=
Quantity
(
type
=
str
)
nomad/metainfo/metainfo2.py
deleted
100644 → 0
View file @
9f4afd3c
from
typing
import
Dict
,
Any
class
MetainfoObjectMeta
(
type
):
def
__new__
(
self
,
name
,
bases
,
dct
):
cls
=
super
().
__new__
(
self
,
name
,
bases
,
dct
)
for
attr_name
,
attr
in
dct
.
items
():
attr_cls
=
attr
.
__class__
if
attr_cls
.
__module__
==
__name__
and
attr_cls
.
__name__
==
'Quantity'
:
attr
.
init
(
cls
,
attr_name
)
if
attr_cls
.
__module__
==
__name__
and
attr_cls
.
__name__
==
'Section'
:
attr
.
init
(
cls
)
return
cls
class
MetainfoObject
(
metaclass
=
MetainfoObjectMeta
):
def
__init__
(
self
):
self
.
m_data
:
Dict
[
str
,
Any
]
=
{}
class
BootstrapQuantity
():
def
__init__
(
self
,
**
kwargs
):
self
.
m_data
=
kwargs
class
Quantity
(
MetainfoObject
):
# m_definition = Section(repeats=True, parent_section=Quantity)
name
=
BootstrapQuantity
(
type
=
str
)
type
=
BootstrapQuantity
(
type
=
type
)
# section
def
__init__
(
self
,
**
kwargs
):
super
().
__init__
()
name
=
kwargs
.
pop
(
'name'
,
None
)
if
name
is
not
None
:
self
.
m_data
.
update
(
name
=
name
)
for
name
,
value
in
kwargs
.
items
():
setattr
(
self
,
name
,
value
)
def
__get__
(
self
,
obj
,
type
=
None
):
return
obj
.
m_data
.
get
(
self
.
m_data
[
'name'
])
def
__set__
(
self
,
obj
,
value
):
obj
.
m_data
[
self
.
m_data
[
'name'
]]
=
value
def
init
(
self
,
cls
,
name
):
self
.
name
=
name
for
name
,
attr
in
Quantity
.
__dict__
.
items
():
if
isinstance
(
attr
,
BootstrapQuantity
):
quantity
=
Quantity
(
name
=
name
,
**
attr
.
m_data
)
setattr
(
Quantity
,
name
,
quantity
)
quantity
.
init
(
Quantity
,
name
)
class
Section
(
MetainfoObject
):
# m_definition = Section(repeats=True, parent_section=Package)
name
=
Quantity
(
type
=
str
)
repeats
=
Quantity
(
type
=
bool
)
# parent_section = Quantity(type=Reference(Section))
# extends = Quantity(type=Reference(Section))
# adds_to = Quantity(type=Reference(Section))
def
__init__
(
self
,
**
kwargs
):
super
().
__init__
()
for
name
,
value
in
kwargs
.
items
():
setattr
(
self
,
name
,
value
)
def
__get__
(
self
,
obj
,
type
=
None
):
return
self
def
init
(
self
,
cls
):
self
.
name
=
cls
.
__name__
self
.
description
=
cls
.
__doc__
.
strip
()
class
System
(
MetainfoObject
):
"""
This is the system documentation
"""
m_definition
=
Section
(
repeats
=
True
)
atom_label
=
Quantity
(
type
=
str
,
shape
=
[
'n'
])
system
=
System
()
system
.
atom_label
=
[
'H'
,
'H'
,
'O'
]
print
(
system
.
m_data
)
print
(
system
.
m_definition
.
m_data
)
print
(
System
.
m_definition
.
name
)
nomad/metainfo/metainfo_tryout.py
0 → 100644
View file @
30136a3b
"""
Some playground to try the CONCEPT.md ideas.
"""
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
():
m_definition
:
Any
=
None
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
:
Union
[
'Enum'
,
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
]