-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Addinstance function #138
base: master
Are you sure you want to change the base?
Addinstance function #138
Changes from all commits
9831e41
0d35ba1
225b36a
0ba81d5
99d0703
590a63a
77736cc
437a2a0
13c5a81
ccffaff
1a136d3
72a4b2f
6140dd5
99d70f4
023e7ba
8ee2b0e
783a91c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# add_instance | ||
|
||
::: oteapi_dlite.strategies.add_instance |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
"""Generic function strategy using DLite storage plugin.""" | ||
# pylint: disable=unused-argument,invalid-name | ||
from typing import TYPE_CHECKING, Optional | ||
|
||
import dlite | ||
from dlite.utils import infer_dimensions | ||
from oteapi.models import AttrDict, FunctionConfig, SessionUpdate | ||
from pydantic import Field | ||
from pydantic.dataclasses import dataclass | ||
|
||
from oteapi_dlite.models import DLiteSessionUpdate | ||
from oteapi_dlite.utils import get_collection, update_collection | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Dict | ||
|
||
from oteapi.interfaces import IFunctionStrategy | ||
|
||
|
||
class AddInstanceConfig(AttrDict): | ||
"""Configuration for adding an instance to the collection.""" | ||
|
||
datamodel: str = Field( | ||
description="ID (URI or UUID) of the datamodel.", | ||
) | ||
property_values: dict = Field( | ||
description="Dict with property values.", | ||
) | ||
dimensions: "Optional[dict]" = Field( | ||
None, | ||
description="Dict with dimension values. If not provided, the " | ||
"dimensions will be inferred from `values`.", | ||
) | ||
label: str = Field( | ||
..., | ||
description="Label of DLite instance to serialise in the collection.", | ||
) | ||
|
||
|
||
class DLiteAddInstanceConfig(FunctionConfig): | ||
"""DLite function strategy config.""" | ||
|
||
configuration: AddInstanceConfig = Field( | ||
..., | ||
description="Strategy-specific configuration for adding " | ||
"an instance to the collection.", | ||
) | ||
|
||
|
||
@dataclass | ||
class DLiteAddInstanceStrategy: | ||
"""DLite function strategy to add an Instance to the collection. | ||
|
||
**Registers strategies**: | ||
|
||
- `("mediaType", "application/vnd.dlite-addinstance")` | ||
|
||
""" | ||
|
||
function_config: DLiteAddInstanceConfig | ||
|
||
def initialize( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be even more useful to add the instance during the initialise phase? |
||
self, | ||
session: "Optional[Dict[str, Any]]" = None, | ||
) -> "SessionUpdate": | ||
"""Initialize.""" | ||
return SessionUpdate() | ||
|
||
def get( | ||
self, session: "Optional[Dict[str, Any]]" = None | ||
) -> "DLiteSessionUpdate": | ||
"""Execute the strategy. | ||
|
||
This method will be called through the strategy-specific endpoint | ||
of the OTE-API Services. | ||
|
||
Parameters: | ||
session: A session-specific dictionary context. | ||
|
||
Returns: | ||
SessionUpdate instance. | ||
""" | ||
config = self.function_config.configuration | ||
|
||
coll = get_collection(session) | ||
datamodel = dlite.get_instance(config.datamodel) | ||
if config.dimensions is None: | ||
dims = infer_dimensions( | ||
datamodel, config.property_values, strict=True | ||
) | ||
else: | ||
dims = config.dimensions | ||
inst = datamodel(dimensions=dims, properties=config.values) | ||
|
||
coll.add(label=config.label, inst=inst) | ||
|
||
update_collection(coll) | ||
return DLiteSessionUpdate(collection_id=coll.uuid) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
otelib~=0.3.0 | ||
pre-commit~=3.3 | ||
pylint~=2.17 | ||
pytest~=7.4 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"7bad2efc-cb40-420b-aef9-2f909d038b46": { | ||
"meta": "http://onto-ns.com/meta/0.1/Result", | ||
"dimensions": { | ||
"natoms": 2, | ||
"ncoords": 3 | ||
}, | ||
"properties": { | ||
"potential_energy": 0, | ||
"forces": [[0, 0, 0], [0, 0, 0]] | ||
} | ||
} | ||
} | ||
Comment on lines
+1
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no point in committing a file that is generated. But more worrying is that the properties are uninitialised. We should test that they are correctly assigned. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
"""Test script for the add_instance strategy - tested via otelib.""" | ||
import os | ||
from pathlib import Path | ||
|
||
import dlite | ||
import numpy as np | ||
from otelib import OTEClient | ||
|
||
# Paths | ||
thisdir = Path(__file__).resolve().parent | ||
testdir = thisdir.parent | ||
entitydir = testdir / "entities" | ||
outdir = testdir / "output" | ||
|
||
os.makedirs(outdir, exist_ok=True) | ||
dlite.storage_path.append(entitydir) | ||
|
||
|
||
values = { | ||
"potential_energy": 3.2e-19, | ||
"forces": [ | ||
[1.2, 2.3, 3.4], | ||
[0.2, 3.4, 4.5], | ||
], | ||
} | ||
|
||
# Create OTE client | ||
client = OTEClient("python") | ||
|
||
add_instance = client.create_function( | ||
functionType="application/vnd.dlite-addinstance", | ||
configuration={ | ||
"datamodel": "http://onto-ns.com/meta/0.1/Result", | ||
"property_values": values, | ||
"label": "result", | ||
}, | ||
) | ||
|
||
|
||
generate = client.create_function( | ||
functionType="application/vnd.dlite-generate", | ||
configuration={ | ||
"driver": "json", | ||
"location": f"{outdir}/test_add_instance.json", | ||
"options": "mode=w", | ||
"label": "result", | ||
}, | ||
) | ||
|
||
pipeline = add_instance >> generate | ||
pipeline.get() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [After running the pipeline, we should test that the file is created and has the expected content. For example import numpy as np
inst = dlite.Instance.from_location("json", f"{outdir}/test_add_instance.json", options="mode=r")
assert np.allclose(inst.potential_energy, 3.2e-19)
assert np.allclose(inst.forses, [[1.2, 2.3, 3.4], [0.2, 3.4, 4.5]]) |
||
|
||
|
||
# Test that the created content is as expected | ||
inst = dlite.Instance.from_location( | ||
driver="json", location=f"{outdir}/test_add_instance.json", options="mode=r" | ||
) | ||
assert np.allclose(inst.potential_energy, 3.2e-19) | ||
assert np.allclose(inst.forses, [[1.2, 2.3, 3.4], [0.2, 3.4, 4.5]]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be mentioned that the data model must be available in
dlite.storage_path
.