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
2ace97ee
Commit
2ace97ee
authored
Sep 24, 2019
by
Markus Scheidgen
Browse files
Continue to detail and document the meta-info.
parent
a3eb56c4
Changes
4
Hide whitespace changes
Inline
Side-by-side
nomad/app/optimade/data.py
View file @
2ace97ee
from
ase.data
import
chemical_symbols
from
elasticsearch_dsl
import
Keyword
,
Integer
,
Float
,
Text
,
InnerDoc
,
Nested
from
nomad.metainfo
import
MObject
,
Section
,
Quantity
,
Enum
,
Units
from
nomad.metainfo
import
MObject
,
Section
,
Quantity
,
Enum
,
units
def
optimade_links
(
section
:
str
):
return
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#%s'
%
section
]
class
ElementRatio
(
InnerDoc
):
...
...
@@ -22,34 +28,34 @@ class Optimade():
class
StructureEntry
(
MObject
):
m_section
=
Section
(
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2'
]
,
links
=
optimade_links
(
'
h.6.2'
)
,
a_flask
=
dict
(
skip_none
=
True
),
a_elastic
=
dict
(
type
=
InnerDoc
))
elements
=
Quantity
(
type
=
Enum
(
chemical_symbols
),
shape
=
[
'1..*'
],
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.1'
]
,
links
=
optimade_links
(
'
h.6.2.1'
)
,
a_elastic
=
dict
(
type
=
Keyword
),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
""" Names of the different elements present in the structure. """
nelements
=
Quantity
(
type
=
int
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.2'
]
,
links
=
optimade_links
(
'
h.6.2.2'
)
,
a_elastic
=
dict
(
type
=
Integer
),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
""" Number of different elements in the structure as an integer. """
elements_ratios
=
Quantity
(
type
=
float
,
shape
=
[
'nelements'
],
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.3'
]
,
links
=
optimade_links
(
'
h.6.2.3'
)
,
a_elastic
=
dict
(
type
=
lambda
:
Nested
(
ElementRatio
),
mapping
=
ElementRatio
.
from_structure_entry
),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
""" Relative proportions of different elements in the structure. """
chemical_formula_descriptive
=
Quantity
(
type
=
str
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.4'
]
,
links
=
optimade_links
(
'
h.6.2.4'
)
,
a_elastic
=
dict
(
type
=
Text
,
other_types
=
dict
(
keyword
=
Keyword
)),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
"""
...
...
@@ -59,7 +65,7 @@ class StructureEntry(MObject):
chemical_formula_reduced
=
Quantity
(
type
=
str
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.5'
]
,
links
=
optimade_links
(
'
h.6.2.5'
)
,
a_elastic
=
dict
(
type
=
Text
,
other_types
=
dict
(
keyword
=
Keyword
)),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
"""
...
...
@@ -69,7 +75,7 @@ class StructureEntry(MObject):
chemical_formula_hill
=
Quantity
(
type
=
str
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.6'
]
,
links
=
optimade_links
(
'
h.6.2.6'
)
,
a_elastic
=
dict
(
type
=
Text
,
other_types
=
dict
(
keyword
=
Keyword
)),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
False
))
"""
...
...
@@ -79,7 +85,7 @@ class StructureEntry(MObject):
chemical_formula_anonymous
=
Quantity
(
type
=
str
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.7'
]
,
links
=
optimade_links
(
'
h.6.2.7'
)
,
a_elastic
=
dict
(
type
=
Text
,
other_types
=
dict
(
keyword
=
Keyword
)),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
"""
...
...
@@ -91,7 +97,7 @@ class StructureEntry(MObject):
dimension_types
=
Quantity
(
type
=
int
,
shape
=
[
3
],
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.8'
]
,
links
=
optimade_links
(
'
h.6.2.8'
)
,
a_elastic
=
dict
(
type
=
Integer
,
mapping
=
lambda
a
:
sum
(
a
.
dimension_types
)),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
"""
...
...
@@ -103,14 +109,14 @@ class StructureEntry(MObject):
"""
lattice_vectors
=
Quantity
(
type
=
float
,
shape
=
[
3
,
3
],
unit
=
U
nits
.
A
ngstrom
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.9'
]
,
type
=
float
,
shape
=
[
3
,
3
],
unit
=
u
nits
.
a
ngstrom
,
links
=
optimade_links
(
'
h.6.2.9'
)
,
a_optimade
=
Optimade
(
query
=
False
,
entry
=
True
))
""" The three lattice vectors in Cartesian coordinates, in ångström (Å). """
cartesian_site_positions
=
Quantity
(
type
=
float
,
shape
=
[
'nsites'
,
3
],
unit
=
U
nits
.
A
ngstrom
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.10'
]
,
type
=
float
,
shape
=
[
'nsites'
,
3
],
unit
=
u
nits
.
a
ngstrom
,
links
=
optimade_links
(
'
h.6.2.10'
)
,
a_optimade
=
Optimade
(
query
=
False
,
entry
=
True
))
"""
Cartesian positions of each site. A site is an atom, a site potentially occupied by
...
...
@@ -120,14 +126,14 @@ class StructureEntry(MObject):
nsites
=
Quantity
(
type
=
int
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.11'
]
,
links
=
optimade_links
(
'
h.6.2.11'
)
,
a_elastic
=
dict
(
type
=
Integer
),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
""" An integer specifying the length of the cartesian_site_positions property. """
species_at_sites
=
Quantity
(
type
=
str
,
shape
=
[
'nsites'
],
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.12'
]
,
links
=
optimade_links
(
'
h.6.2.12'
)
,
a_optimade
=
Optimade
(
query
=
False
,
entry
=
True
))
"""
Name of the species at each site (where values for sites are specified with the same
...
...
@@ -139,7 +145,7 @@ class StructureEntry(MObject):
structure_features
=
Quantity
(
type
=
Enum
([
'disorder'
,
'unknown_positions'
,
'assemblies'
]),
shape
=
[
'1..*'
],
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.15'
]
,
links
=
optimade_links
(
'
h.6.2.15'
)
,
a_elastic
=
dict
(
type
=
Keyword
),
a_optimade
=
Optimade
(
query
=
True
,
entry
=
True
))
"""
...
...
@@ -162,7 +168,7 @@ class Species(MObject):
m_section
=
Section
(
repeats
=
True
,
parent
=
StructureEntry
.
m_section
,
links
=
[
'https://github.com/Materials-Consortia/OPTiMaDe/blob/develop/optimade.md#
h.6.2.13'
]
)
links
=
optimade_links
(
'
h.6.2.13'
)
)
name
=
Quantity
(
type
=
str
,
...
...
@@ -209,7 +215,7 @@ class Species(MObject):
species).
"""
mass
=
Quantity
(
type
=
float
,
unit
=
U
nits
.
amu
,
a_optimade
=
dict
(
entry
=
'optional'
))
mass
=
Quantity
(
type
=
float
,
unit
=
u
nits
.
amu
,
a_optimade
=
dict
(
entry
=
'optional'
))
original_name
=
Quantity
(
type
=
str
,
a_optimade
=
dict
(
entry
=
'optional'
))
"""
...
...
nomad/metainfo/__init__.py
View file @
2ace97ee
from
.metainfo
import
MObject
,
Section
,
Quantity
,
Enum
,
U
nits
from
.metainfo
import
MObject
,
Section
,
Quantity
,
Enum
,
u
nits
nomad/metainfo/metainfo.py
View file @
2ace97ee
...
...
@@ -136,7 +136,9 @@ See the reference of classes :class:`Section` and :class:`Quantities` for detail
from
typing
import
Type
,
TypeVar
,
Union
,
Tuple
,
Iterable
,
List
,
Any
,
Dict
,
cast
import
sys
import
inspect
from
pint.unit
import
_Unit
from
pint
import
UnitRegistry
__module__
=
sys
.
modules
[
__name__
]
MObjectBound
=
TypeVar
(
'MObjectBound'
,
bound
=
'MObject'
)
...
...
@@ -200,16 +202,20 @@ class MObject(metaclass=MObjectMeta):
if possible.
"""
m_section
:
'Section'
=
None
def
__init__
(
self
,
m_section
:
'Section'
=
None
,
m_parent
:
'MObject'
=
None
,
**
kwargs
):
self
.
m_section
:
'Section'
=
m_section
self
.
m_parent
:
'MObject'
=
m_parent
self
.
m_parent_index
=
-
1
self
.
m_data
=
dict
(
**
kwargs
)
cls
=
self
.
__class__
if
self
.
m_section
is
None
:
self
.
m_section
=
getattr
(
self
.
__class__
,
'm_section'
,
None
)
else
:
assert
self
.
m_section
==
getattr
(
self
.
__class__
,
'm_section'
,
self
.
m_section
),
\
self
.
m_section
=
cls
.
m_section
if
cls
.
m_section
is
not
None
:
assert
self
.
m_section
==
cls
.
m_section
,
\
'Section class and section definition must match'
@
classmethod
...
...
@@ -218,36 +224,45 @@ class MObject(metaclass=MObjectMeta):
# no initialization during bootstrapping, will be done maunally
return
m_section
=
getattr
(
cls
,
'
m_section
'
,
None
)
if
m_section
is
None
:
m_section
=
cls
.
m_section
if
m_section
is
None
and
cls
!=
MObject
:
m_section
=
Section
()
setattr
(
cls
,
'm_section'
,
m_section
)
m_section
.
name
=
cls
.
__name__
if
cls
.
__doc__
is
not
None
:
m_section
.
description
=
inspect
.
cleandoc
(
cls
.
__doc__
)
m_section
.
section_cls
=
cls
for
name
,
value
in
cls
.
__dict__
.
items
():
if
isinstance
(
value
,
Quantity
):
value
.
name
=
name
for
name
,
attr
in
cls
.
__dict__
.
items
():
if
isinstance
(
attr
,
Quantity
):
attr
.
name
=
name
if
attr
.
__doc__
is
not
None
:
attr
.
description
=
inspect
.
cleandoc
(
attr
.
__doc__
)
# manual manipulation of m_data due to bootstrapping
m_section
.
m_data
.
setdefault
(
'Quantity'
,
[]).
append
(
value
)
m_section
.
m_data
.
setdefault
(
'Quantity'
,
[]).
append
(
attr
)
@
staticmethod
def
_
_type_check
(
definition
:
'Quantity'
,
value
:
Any
,
check_item
:
bool
=
False
):
def
m
_type_check
(
definition
:
'Quantity'
,
value
:
Any
,
check_item
:
bool
=
False
):
"""Checks if the value fits the given quantity in type and shape; raises
ValueError if not."""
TypeError if not."""
if
value
is
None
and
not
check_item
and
definition
.
default
is
None
:
# Allow the default None value even if it would violate the type
return
def
check_value
(
value
):
if
isinstance
(
definition
.
type
,
Enum
):
if
value
not
in
definition
.
type
:
raise
Valu
eError
(
'Not one of the enum values.'
)
raise
Typ
eError
(
'Not one of the enum values.'
)
elif
isinstance
(
definition
.
type
,
type
):
if
not
isinstance
(
value
,
definition
.
type
):
raise
Valu
eError
(
'Value has wrong type.'
)
raise
Typ
eError
(
'Value has wrong type.'
)
elif
isinstance
(
definition
.
type
,
Section
):
if
not
isinstance
(
value
,
MObject
)
or
value
.
m_section
!=
definition
.
type
:
raise
Valu
eError
(
'The value is not a section of wrong section definition'
)
raise
Typ
eError
(
'The value is not a section of wrong section definition'
)
else
:
raise
Exception
(
'Invalid quantity type: %s'
%
str
(
definition
.
type
))
...
...
@@ -263,7 +278,7 @@ class MObject(metaclass=MObjectMeta):
elif
len
(
shape
)
==
1
:
if
not
isinstance
(
value
,
list
):
raise
Valu
eError
(
'Wrong shape'
)
raise
Typ
eError
(
'Wrong shape'
)
for
item
in
value
:
check_value
(
item
)
...
...
@@ -369,7 +384,7 @@ class MObject(metaclass=MObjectMeta):
quantity
=
self
.
__resolve_quantity
(
definition
)
MObject
.
_
_type_check
(
quantity
,
value
,
check_item
=
True
)
MObject
.
m
_type_check
(
quantity
,
value
,
check_item
=
True
)
m_data_values
=
self
.
m_data
.
setdefault
(
quantity
.
name
,
[])
m_data_values
.
append
(
value
)
...
...
@@ -380,7 +395,7 @@ class MObject(metaclass=MObjectMeta):
quantity
=
self
.
__resolve_quantity
(
definition
)
for
value
in
values
:
MObject
.
_
_type_check
(
quantity
,
value
,
check_item
=
True
)
MObject
.
m
_type_check
(
quantity
,
value
,
check_item
=
True
)
m_data_values
=
self
.
m_data
.
setdefault
(
quantity
.
name
,
[])
for
value
in
values
:
...
...
@@ -422,40 +437,143 @@ class MObject(metaclass=MObjectMeta):
return
'%s:%s'
%
(
name
,
m_section_name
)
# M3
# M3, the definitions that are used to write definitions. These are the section definitions
# for sections Section and Quantity.They define themselves; i.e. the section definition
# for Section is the same section definition.
# Due to this circular nature (hen-egg-problem), the classes for sections Section and
# Quantity do only contain placeholder for their own section and quantity definitions.
# These placeholder are replaced, once the necessary classes are defined. This process
# is referred to as 'bootstrapping'.
class
Quantity
(
MObject
):
m_section
:
'Section'
=
None
"""Used to define quantities that store a certain piece of (meta-)data.
Quantities are the basic building block with meta-info data. The Quantity class is
used to define quantities within sections. A quantity definition
is a (physics) quantity with name, type, shape, and potentially a unit.
In Python terms, quantities are descriptors. Descriptors define how to get, set, and
delete values for a object attribute. Meta-info descriptors ensure that
type and shape fit the set values.
"""
name
:
'Quantity'
=
None
""" The name of the quantity. Must be unique within a section. """
description
:
'Quantity'
=
None
""" An optional human readable description. """
links
:
'Quantity'
=
None
""" A list of URLs to external resource that describe this definition. """
type
:
'Quantity'
=
None
""" The type of the quantity.
Can be one of the following:
- a build-in Python type, e.g. ``int``, ``str``, ``any``
- an instance of :class:`Enum`, e.g. ``Enum(['one', 'two', 'three'])
- a instance of Section, i.e. a section definition. This will define a reference
- the Python typing ``Any`` to denote an arbitrary type
- a Python class, e.g. ``datetime``
In the NOMAD CoE meta-info this was basically the ``dTypeStr``.
"""
shape
:
'Quantity'
=
None
""" The shape of the quantity that defines its dimensionality.
__name
=
property
(
lambda
self
:
self
.
m_data
[
'name'
])
A shape is a list, where each item defines a dimension. Each dimension can be:
- an integer that defines the exact size of the dimension, e.g. ``[3]`` is the
shape of a spacial vector
- the name of an int typed quantity in the same section
- a range specification as string build from a lower bound (i.e. int number),
and an upper bound (int or ``*`` denoting arbitrary large), e.g. ``'0..*'``, ``'1..3'``
"""
unit
:
'Quantity'
=
None
""" The optional physics unit for this quantity.
default
=
property
(
lambda
self
:
None
)
Units are given in `pint` units. Pint is a Python package that defines units and
their algebra. There is a default registry :data:`units` that you can use.
Example units are: ``units.m``, ``units.m / units.s ** 2``.
"""
default
:
'Quantity'
=
None
""" The default value for this quantity. """
# Some quantities of Quantity cannot be read as normal quantities due to bootstraping.
# Those can be accessed internally through the following replacement properties that
# read directly from m_data.
__name
=
property
(
lambda
self
:
self
.
m_data
[
'name'
])
__default
=
property
(
lambda
self
:
self
.
m_data
.
get
(
'default'
,
None
))
def
__get__
(
self
,
obj
,
type
=
None
):
return
obj
.
m_data
[
self
.
__name
]
if
obj
is
None
:
# class (def) attribute case
return
self
# object (instance) attribute case
try
:
return
obj
.
m_data
[
self
.
__name
]
except
KeyError
:
return
self
.
__default
def
__set__
(
self
,
obj
,
value
):
MObject
.
__dict__
[
'_MObject__type_check'
].
__get__
(
MObject
)(
self
,
value
)
if
obj
is
None
:
# class (def) case
raise
KeyError
(
'Cannot overwrite quantity definition. Only values can be set.'
)
# object (instance) case
MObject
.
m_type_check
(
self
,
value
)
obj
.
m_data
[
self
.
__name
]
=
value
def
__delete__
(
self
,
obj
):
if
obj
is
None
:
# class (def) case
raise
KeyError
(
'Cannot delete quantity definition. Only values can be deleted.'
)
# object (instance) case
del
obj
.
m_data
[
self
.
__name
]
class
Section
(
MObject
):
m_section
:
'Section'
=
None
"""Used to define section that organize meta-info data into containment hierarchies.
Section definitions determine what quantities and sub-sections can appear in a section
instance. A section instance itself can appear potentially many times in its parent
section. See :data:`repeats` and :data:`parent`.
In Python terms, sections are classes. Sub-sections and quantities are attribute of
respective instantiating objects. For each section class there is a corresponding
:class:`Section` instance that describes this class as a section. This instance
is referred to as 'section definition' in contrast to the Python class that we call
'section class'.
"""
section_cls
:
Type
[
MObject
]
=
None
""" The section class that corresponse to this section definition. """
name
:
'Quantity'
=
None
""" The name of the section. """
description
:
'Quantity'
=
None
""" A human readable description of the section. """
links
:
'Quantity'
=
None
""" A list of URLs to external resource that describe this definition. """
repeats
:
'Quantity'
=
None
""" Wether instances of this section can occur repeatedly in the parent section. """
parent
:
'Quantity'
=
None
extends
:
'Quantity'
=
None
""" The section definition for parents.
__all_instances
:
List
[
'Section'
]
=
[]
Instances of this section can only occur in instances of the given parent.
"""
default
=
property
(
lambda
self
:
[]
if
self
.
repeats
else
None
)
__all_instances
:
List
[
'Section'
]
=
[]
def
__init__
(
self
,
**
kwargs
):
# The mechanism that produces default values, depends on parent. Without setting
...
...
@@ -493,21 +611,48 @@ class Section(MObject):
for
sub_section
in
Section
.
__all_instances
if
sub_section
.
parent
==
self
}
def
add_quantity
(
self
,
quantity
:
Quantity
):
"""
Adds the given quantity to this section.
Allows to add a quantity to a section definition outside the corresponding
section class.
.. code-block:: Python
class System(MObject):
pass
System.m_section.add_quantity(Quantity(name='n_atoms', type=int))
This will add the quantity definition to this section definition,
and add the respective Python descriptor as an attribute to this class.
"""
quantities
=
self
.
m_data
.
setdefault
(
'Quantity'
,
[])
quantities
.
append
(
quantity
)
setattr
(
self
.
section_cls
,
quantity
.
name
,
quantity
)
Section
.
m_section
=
Section
(
repeats
=
True
,
name
=
'Section'
)
Section
.
m_section
.
m_section
=
Section
.
m_section
Section
.
m_section
.
section_cls
=
Section
Section
.
name
=
Quantity
(
type
=
str
,
name
=
'name'
)
Section
.
repeats
=
Quantity
(
type
=
bool
,
name
=
'repeats'
)
Section
.
description
=
Quantity
(
type
=
str
,
name
=
'description'
)
Section
.
links
=
Quantity
(
type
=
str
,
shape
=
[
'0..*'
],
name
=
'links'
)
Section
.
repeats
=
Quantity
(
type
=
bool
,
name
=
'repeats'
,
default
=
False
)
Section
.
parent
=
Quantity
(
type
=
Section
.
m_section
,
name
=
'parent'
)
Section
.
extends
=
Quantity
(
type
=
Section
.
m_section
,
shape
=
[
'0..*'
],
name
=
'extends'
)
Quantity
.
m_section
=
Section
(
repeats
=
True
,
parent
=
Section
.
m_section
,
name
=
'Quantity'
)
Quantity
.
m_section
.
section_cls
=
Quantity
Quantity
.
name
=
Quantity
(
type
=
str
,
name
=
'name'
)
Quantity
.
description
=
Quantity
(
type
=
str
,
name
=
'description'
)
Quantity
.
links
=
Quantity
(
type
=
str
,
shape
=
[
'0..*'
],
name
=
'links'
)
Quantity
.
type
=
Quantity
(
type
=
Union
[
type
,
Enum
,
Section
],
name
=
'type'
)
Quantity
.
shape
=
Quantity
(
type
=
Union
[
str
,
int
],
shape
=
[
'0..*'
],
name
=
'shape'
)
Quantity
.
unit
=
Quantity
(
type
=
_Unit
)
Quantity
.
default
=
Quantity
(
type
=
Any
,
default
=
None
)
class
Package
(
MObject
):
...
...
@@ -517,18 +662,5 @@ class Package(MObject):
Section
.
m_section
.
parent
=
Package
.
m_section
class
Definition
(
MObject
):
m_section
=
Section
(
extends
=
[
Section
.
m_section
,
Quantity
.
m_section
,
Package
.
m_section
])
description
=
Quantity
(
type
=
str
)
class
Unit
:
pass
class
Units
:
Angstrom
=
Unit
()
amu
=
Unit
()
units
=
UnitRegistry
()
""" The default pint unit registry that should be used to give units to quantity definitions. """
tests/test_metainfo.py
View file @
2ace97ee
...
...
@@ -16,6 +16,8 @@ from nomad.metainfo.metainfo import MObject, Section, Quantity
class
TestM3
:
""" Test for meta-info definition that are used to define other definitions. """
def
test_section
(
self
):
assert
Section
.
m_section
==
Section
.
m_section
.
m_section
assert
Section
.
m_section
.
name
==
'Section'
...
...
@@ -27,6 +29,7 @@ class TestM3:
class
TestPureReflection
:
""" Test for using meta-info instances without knowing/using the respective definitions. """
def
test_instantiation
(
self
):
test_section_def
=
Section
(
name
=
'TestSection'
)
...
...
@@ -40,36 +43,82 @@ class TestPureReflection:
class
Run
(
MObject
):
m_section
=
Section
()
""" This is the description.
And some more description.
"""
code_name
=
Quantity
(
type
=
str
)
""" The code_name description. """
class
System
(
MObject
):
m_section
=
Section
(
repeats
=
True
,
parent
=
Run
.
m_section
)
n_atoms
=
Quantity
(
type
=
int
)
n_atoms
=
Quantity
(
type
=
int
,
default
=
0
)
atom_label
=
Quantity
(
type
=
str
,
shape
=
[
'n_atoms'
])
class
Parsing
(
MObject
):
m_section
=
Section
(
parent
=
Run
.
m_section
)
class
TestM2
:
""" Test for meta-info definitions. """
def
test_default_section_def
(
self
):
""" A section class without an explicit section def must set a default section def. """
assert
Run
.
m_section
is
not
None
assert
Run
.
m_section
.
name
==
'Run'
assert
not
Run
.
m_section
.
repeats
assert
Run
.
m_section
.
parent
is
None
def
test_quantities
(
self
):
assert
len
(
Run
.
m_section
.
quantities
)
==
1
assert
Run
.
m_section
.
quantities
[
'code_name'
]
==
Run
.
__dict__
[
'code_name'
]
def
test_sub_sections
(
self
):
assert
len
(
Run
.
m_section
.
sub_sections
)
==
1
assert
len
(
Run
.
m_section
.
sub_sections
)
==
2
assert
Run
.
m_section
.
sub_sections
[
'System'
]
==
System
.
m_section
def
test_attributes
(
self
):
assert
len
(
Run
.
m_section
.
attributes
)
==
2
assert
len
(
Run
.
m_section
.
attributes
)
==
3
assert
Run
.
m_section
.
attributes
[
'System'
]
==
System
.
m_section
assert
Run
.
m_section
.
attributes
[
'code_name'
]
==
Run
.
__dict__
[
'code_name'
]
def
test_get_quantity_def
(
self
):
assert
System
.
n_atoms
==
System
.
m_section
.
attributes
[
'n_atoms'
]
def
test_add_quantity
(
self
):
System
.
m_section
.
add_quantity
(
Quantity
(
name
=
'test'
,
type
=
str
))
system
=
System
()
system
.
test
=
'test_value'
assert
'test'
in
system
.
m_data
assert
system
.
test
==
'test_value'
assert
getattr
(
System
,
'test'
)
==
System
.
m_section
.
quantities
[
'test'
]
def
test_section_name
(
self
):
assert
Run
.
m_section
.
name
==
'Run'
def
test_quantity_name
(
self
):
assert
Run
.
code_name
.
name
==
'code_name'
def
test_section_description
(
self
):
assert
Run
.
m_section
.
description
is
not
None
assert
Run
.
m_section
.
description
.
strip
()
==
Run
.
m_section
.
description
.
strip
()
def
test_quantity_description
(
self
):
assert
Run
.
code_name
.
description
is
not
None
assert
Run
.
code_name
.
description
.
strip
()
==
Run
.
code_name
.
description
.
strip
()
class
TestM1
:
""" Test for meta-info instances. """
def
test_run
(
self
):
class
Run
(
MObject
):
m_section
=
Section
()
pass
run
=
Run
()
...
...
@@ -86,3 +135,58 @@ class TestM1:
system
.
atom_labels
=
[
'H'
]
assert
len
(
system
.
atom_labels
)
==
1
assert
len
(
system
.
m_data
)
==
1
def
test_defaults
(
self
):
assert
System
().
n_atoms
==
0
assert
System
().
atom_label
is
None
try
:
System
().
does_not_exist
assert
False
,
'Supposed unreachable'
except
AttributeError
:
pass
else
: