Basic use of entities#

Load an entity#

A test entity:

description: A bare-bones entity for testing.
dimensions:
  N: Number of elements.
identity: http://onto-ns.com/s7/0.1.0/MolecularSpecies
properties:
  atom:
    description: An atom.
    shape:
    - N
    type: string
  electrons:
    description: Number of electrons.
    shape:
    - N
    type: int
  mass:
    description: Atomic mass.
    shape:
    - N
    type: float
    unit: amu
  radius:
    description: Atomic radius.
    shape:
    - N
    type: float
    unit: Å

Load the entity as a SOFT7Entity class.

from s7.pydantic_models.soft7_entity import SOFT7Entity

entity = SOFT7Entity(**RAW_TEST_ENTITY)

print(entity)
identity=Url('http://onto-ns.com/s7/0.1.0/MolecularSpecies') description='A bare-bones entity for testing.' dimensions={'N': 'Number of elements.'} properties={'atom': SOFT7EntityProperty(type='string', description='An atom.', shape=['N'], unit=None), 'electrons': SOFT7EntityProperty(type='int', description='Number of electrons.', shape=['N'], unit=None), 'mass': SOFT7EntityProperty(type='float', description='Atomic mass.', shape=['N'], unit='amu'), 'radius': SOFT7EntityProperty(type='float', description='Atomic radius.', shape=['N'], unit='Å')}

Generate a Python class from the entity#

Generate a custom Python class based on the entity. It will be possible to create instances of this class, based on the properties and dimensions of the entity.

from s7.factories import create_entity

MolecularSpecies = create_entity(entity)

print(f"Class: {MolecularSpecies}")
print(MolecularSpecies.__doc__)
print("JSON Schema:")
print(MolecularSpecies.model_json_schema())
Class: <class 's7.factories.generated_classes.MolecularSpeciesEntity'>
MolecularSpecies

    A bare-bones entity for testing.

    SOFT7 Entity Metadata:
        Identity: http://onto-ns.com/s7/0.1.0/MolecularSpecies

        Namespace: http://onto-ns.com/s7
        Version: 0.1.0
        Name: MolecularSpecies

    Dimensions:
        N (int): Number of elements.

    Attributes:
        atom (list[str]): An atom.
        electrons (list[int]): Number of electrons.
        mass (list[float]): Atomic mass.
        radius (list[float]): Atomic radius.

    
JSON Schema:
{'$defs': {'MolecularSpeciesEntityDimensions': {'additionalProperties': False, 'description': 'MolecularSpeciesDimensions\n\nDimensions for the MolecularSpecies SOFT7 data source.\n\nSOFT7 Entity: http://onto-ns.com/s7/0.1.0/MolecularSpecies\n\nAttributes:\n    N (int): Number of elements.', 'properties': {'N': {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'default': None, 'description': 'Number of elements.', 'title': 'N'}}, 'title': 'MolecularSpeciesEntityDimensions', 'type': 'object'}, 'MolecularSpeciesEntityProperties': {'additionalProperties': False, 'description': 'MolecularSpeciesProperties\n\nProperties for the MolecularSpecies SOFT7 data source.\n\nSOFT7 Entity: http://onto-ns.com/s7/0.1.0/MolecularSpecies\n\nAttributes:\n    atom (list[str]): An atom.\n    electrons (list[int]): Number of electrons.\n    mass (list[float]): Atomic mass.\n    radius (list[float]): Atomic radius.', 'properties': {'atom': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'default': None, 'description': 'An atom.', 'title': 'atom', 'x-soft7-shape': ['N']}, 'electrons': {'anyOf': [{'items': {'type': 'integer'}, 'type': 'array'}, {'type': 'null'}], 'default': None, 'description': 'Number of electrons.', 'title': 'electrons', 'x-soft7-shape': ['N']}, 'mass': {'anyOf': [{'items': {'type': 'number'}, 'type': 'array'}, {'type': 'null'}], 'default': None, 'description': 'Atomic mass.', 'title': 'mass', 'x-soft7-shape': ['N'], 'x-soft7-unit': 'amu'}, 'radius': {'anyOf': [{'items': {'type': 'number'}, 'type': 'array'}, {'type': 'null'}], 'default': None, 'description': 'Atomic radius.', 'title': 'radius', 'x-soft7-shape': ['N'], 'x-soft7-unit': 'Å'}}, 'title': 'MolecularSpeciesEntityProperties', 'type': 'object'}}, 'additionalProperties': False, 'description': 'MolecularSpecies\n\nA bare-bones entity for testing.\n\nSOFT7 Entity Metadata:\n    Identity: http://onto-ns.com/s7/0.1.0/MolecularSpecies\n\n    Namespace: http://onto-ns.com/s7\n    Version: 0.1.0\n    Name: MolecularSpecies\n\nDimensions:\n    N (int): Number of elements.\n\nAttributes:\n    atom (list[str]): An atom.\n    electrons (list[int]): Number of electrons.\n    mass (list[float]): Atomic mass.\n    radius (list[float]): Atomic radius.', 'properties': {'dimensions': {'$ref': '#/$defs/MolecularSpeciesEntityDimensions', 'description': 'The MolecularSpecies SOFT7 entity dimensions.'}, 'properties': {'$ref': '#/$defs/MolecularSpeciesEntityProperties', 'description': 'The MolecularSpecies SOFT7 entity properties.'}}, 'required': ['dimensions', 'properties'], 'title': 'MolecularSpeciesEntity', 'type': 'object'}

The entity is still accessible under the entity class attribute:

print(MolecularSpecies.entity)
identity=Url('http://onto-ns.com/s7/0.1.0/MolecularSpecies') description='A bare-bones entity for testing.' dimensions={'N': 'Number of elements.'} properties={'atom': SOFT7EntityProperty(type='string', description='An atom.', shape=['N'], unit=None), 'electrons': SOFT7EntityProperty(type='int', description='Number of electrons.', shape=['N'], unit=None), 'mass': SOFT7EntityProperty(type='float', description='Atomic mass.', shape=['N'], unit='amu'), 'radius': SOFT7EntityProperty(type='float', description='Atomic radius.', shape=['N'], unit='Å')}

When generating a new class, it can be imported from the s7.factories.generated_classes module:

from s7.factories import generated_classes

# Here we are printing all the classes that were generated by the factory.
# More specifically, we are printing all the classes that are in the generated_classes
# module.
print([_ for _ in generated_classes.__dict__.values() if isinstance(_, type)])
[<class 's7.factories.generated_classes.MolecularSpeciesEntityDimensions'>, <class 's7.factories.generated_classes.MolecularSpeciesEntityProperties'>, <class 's7.factories.generated_classes.MolecularSpeciesEntity'>]

Note that along with the generated class (here MolecularSpeciesEntity), two more classes are generated to represent the entity’s properties (here MolecularSpeciesEntityProperties) and dimensions (here MolecularSpeciesEntityDimensions).

One can even import the named entity classes directly from the s7.factories.generated_classes module:

from s7.factories.generated_classes import MolecularSpeciesEntity

print(MolecularSpeciesEntity)
print(
    "The imported 'MolecularSpeciesEntity' class is exactly the same as the previously "
    f"generated 'MolecularSpecies' class: {MolecularSpeciesEntity == MolecularSpecies}"
)
print(MolecularSpeciesEntity.__doc__)
print(MolecularSpeciesEntity.entity)
<class 's7.factories.generated_classes.MolecularSpeciesEntity'>
The imported 'MolecularSpeciesEntity' class is exactly the same as the previously generated 'MolecularSpecies' class: True
MolecularSpecies

    A bare-bones entity for testing.

    SOFT7 Entity Metadata:
        Identity: http://onto-ns.com/s7/0.1.0/MolecularSpecies

        Namespace: http://onto-ns.com/s7
        Version: 0.1.0
        Name: MolecularSpecies

    Dimensions:
        N (int): Number of elements.

    Attributes:
        atom (list[str]): An atom.
        electrons (list[int]): Number of electrons.
        mass (list[float]): Atomic mass.
        radius (list[float]): Atomic radius.

    
identity=Url('http://onto-ns.com/s7/0.1.0/MolecularSpecies') description='A bare-bones entity for testing.' dimensions={'N': 'Number of elements.'} properties={'atom': SOFT7EntityProperty(type='string', description='An atom.', shape=['N'], unit=None), 'electrons': SOFT7EntityProperty(type='int', description='Number of electrons.', shape=['N'], unit=None), 'mass': SOFT7EntityProperty(type='float', description='Atomic mass.', shape=['N'], unit='amu'), 'radius': SOFT7EntityProperty(type='float', description='Atomic radius.', shape=['N'], unit='Å')}

Create an instance of the custom class#

Load test data that has been pre-formatted to match the data structure of an entity, i.e., it has been written as a dictionary/object with the top keys dimensions and properties.

TEST_ENTITY_DATA_PATH = STATIC_TEST_DIR / "soft_datasource_entity_test_data.yaml"

RAW_TEST_ENTITY_DATA: dict[str, Any] = yaml.safe_load(TEST_ENTITY_DATA_PATH.read_text())

print(yaml.safe_dump(RAW_TEST_ENTITY_DATA))
dimensions:
  N: 3
properties:
  atom:
  - H
  - He
  - Li
  electrons:
  - 1
  - 2
  - 3
  mass:
  - 1.008
  - 4.0026
  - 6.94
  radius:
  - 0.37
  - 0.32
  - 1.45
entity_data = MolecularSpecies(**RAW_TEST_ENTITY_DATA)

print(entity_data)
dimensions=MolecularSpeciesEntityDimensions(N=3) properties=MolecularSpeciesEntityProperties(atom=['H', 'He', 'Li'], electrons=[1, 2, 3], mass=[1.008, 4.0026, 6.94], radius=[0.37, 0.32, 1.45])

While the entity is still accessible under the entity class attribute for the instance, when serialized to JSON or a dictionary utilizing the standard serialization methods for a pydantic model, only dimensions and properties are included:

print("Python dictionary:")
print(entity_data.model_dump())

print("JSON:")
print_json(entity_data.model_dump_json())
Python dictionary:
{'dimensions': {'N': 3}, 'properties': {'atom': ['H', 'He', 'Li'], 'electrons': [1, 2, 3], 'mass': [1.008, 4.0026, 6.94], 'radius': [0.37, 0.32, 1.45]}}
JSON:
{"dimensions":{"N":3},"properties":{"atom":["H","He","Li"],"electrons":[1,2,3],"mass":[1.008,4.0026,6.94],"radius":[0.37,0.32,1.45]}}