Skip to content

Commit

Permalink
Merge pull request #18 from openkim/disclaimer
Browse files Browse the repository at this point in the history
Disclaimer
  • Loading branch information
yafshar authored Jun 4, 2024
2 parents 8e01df8 + 3b34458 commit d3b3a6a
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 14 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ Creating property instances::
"instance-id" 2
}
]

>>> kim_property_create(1, 'cohesive-energy-relation-cubic-crystal', property_disclaimer="This is an example disclaimer.")
'[{"property-id" "tag:[email protected],2014-04-15:property/cohesive-energy-relation-cubic-crystal" "instance-id" 1 "disclaimer" "This is an example disclaimer."}]'

>>> property_inst = kim_property_create(1, 'cohesive-energy-relation-cubic-crystal')

>>> kim_property_create(2, 'atomic-mass', property_inst, "This is an example disclaimer for atomic-mass.")
'[{"property-id" "tag:[email protected],2014-04-15:property/cohesive-energy-relation-cubic-crystal" "instance-id" 1} {"property-id" "tag:[email protected],2016-05-11:property/atomic-mass" "instance-id" 2 "disclaimer" "This is an example disclaimer for atomic-mass."}]'
````

A property instance is stored in a subset of the KIM-EDN format as described in
Expand Down Expand Up @@ -170,6 +178,26 @@ An instance of this property could be created like so::
}
}
]

>>> property_inst = kim_property_create(1, 'cohesive-energy-relation-cubic-crystal')
>>> property_inst = kim_property_modify(property_inst, 1,
"disclaimer", "This is an example disclaimer.",
"key", "short-name",
"source-value", "1", "fcc")
>>> property_inst_obj = kim_edn.loads(property_inst)
>>> print(kim_edn.dumps(property_inst_obj, indent=4))
[
{
"property-id" "tag:[email protected],2014-04-15:property/cohesive-energy-relation-cubic-crystal"
"instance-id" 1
"disclaimer" "This is an example disclaimer."
"short-name" {
"source-value" [
"fcc"
]
}
}
]
````

For cases where there are multiple keys or a key receives an array of values
Expand Down
13 changes: 12 additions & 1 deletion kim_property/create.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Create module."""

from os.path import isfile
from typing import Optional

import kim_edn

Expand Down Expand Up @@ -68,7 +69,11 @@ def unset_property_id(property_id):
NEW_PROPERTY_IDS = None


def kim_property_create(instance_id, property_name, property_instances=None):
def kim_property_create(
instance_id: int,
property_name: str,
property_instances: Optional[str] = None,
property_disclaimer: Optional[str] = None):
"""Create a new kim property instance.
It takes as input the property instance ID and property definition name
Expand Down Expand Up @@ -112,6 +117,9 @@ def kim_property_create(instance_id, property_name, property_instances=None):
the current working directory) of the file to be opened
property_instances {string} -- A string containing the serialized
KIM-EDN formatted property instances. (default: {None})
property_disclaimer {string} -- A string containing an optional
statement of applicability of the data contained in this property
instance. (default: {None})
Returns:
string -- serialized KIM-EDN formatted property instances.
Expand Down Expand Up @@ -209,6 +217,9 @@ def kim_property_create(instance_id, property_name, property_instances=None):

new_property_instance["instance-id"] = instance_id

if property_disclaimer is not None:
new_property_instance["disclaimer"] = property_disclaimer

# Add the newly created property instance to the collection
kim_property_instances.append(new_property_instance)

Expand Down
25 changes: 18 additions & 7 deletions kim_property/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

__all__ = [
"required_keys",
"optional_keys",
"standard_keys",
"get_property_id_path",
"check_instance_id_format",
Expand All @@ -33,6 +34,15 @@
# A Property Instance must contain the following required key-value pairs:
required_keys = ("property-id", "instance-id")

# A Property Instance may contain the following optional key-value pairs:
optional_keys = (
# A string containing an optional statement of applicability of the data
# contained in this Property Instance. For a Prediction, this can be
# provided by the Test, while for an item of Reference Data, this can be
# provided by the contributor.
"disclaimer",
)

# The required fields list above are followed by an unordered set of
# key-map pairs. Each key is associated with a map which must contain
# the following standard keys-value pairs:
Expand Down Expand Up @@ -138,10 +148,11 @@ def check_instance_id_format(instance_id, _m=INSTANCE_ID.match):
msg += 'format specification (an integer equal to or '
msg += 'greater than 1).'
raise KIMPropertyError(msg)
else:
msg = 'the "instance-id" value is not an `int` '
msg += 'and doesn\'t meet the format specification.'
raise KIMPropertyError(msg)
return

msg = 'the "instance-id" value is not an `int` and doesn\'t meet the '
msg += 'format specification.'
raise KIMPropertyError(msg)


# checks for optional keys
Expand Down Expand Up @@ -180,7 +191,7 @@ def check_optional_key_source_value_scalar(source_value_key, value_type):
msg = 'input to the function doesn\'t comply with '
msg += 'the defined variable type.\n'
msg += 'The variable type can be one of ::\n'
msg += '"string", "float", "int", "bool", or "file".'
msg += '`string`, `float`, `int`, `bool`, or `file`.'
raise KIMPropertyError(msg)


Expand Down Expand Up @@ -471,7 +482,7 @@ def check_property_instances(fi, fp=None, fp_path=None, _m=KEY_FORMAT.match):

# Check optional fields.
for k in pi:
if k not in required_keys:
if k not in required_keys + optional_keys:
if k in pd:
check_instance_optional_key_map(k, pi[k], pd[k], _m=_m)
else:
Expand Down Expand Up @@ -548,7 +559,7 @@ def check_property_instances(fi, fp=None, fp_path=None, _m=KEY_FORMAT.match):

# Check optional fields.
for k in pi_:
if k not in required_keys:
if k not in required_keys + optional_keys:
if k in pd:
check_instance_optional_key_map(
k, pi_[k], pd[k], _m=_m)
Expand Down
42 changes: 36 additions & 6 deletions kim_property/modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .definition import \
get_optional_key_extent_ndimensions, \
get_optional_key_extent_shape
from .instance import standard_keys
from .instance import standard_keys, check_instance_id_format
from .create import get_properties

__all__ = [
Expand Down Expand Up @@ -82,12 +82,20 @@ def kim_property_modify(property_instances, instance_id, *argv): # noqa: C901
"source-std-uncert-value", "1:5", "0.002", "0.0001", "0.00001", "0.0012", "0.00015",
"source-unit", "eV",
"digits", "5")
>>> str = kim_property_modify(str, 1,
"disclaimer", "This is an example disclaimer.")
>>> str = kim_property_modify(str, 1,
"key", "basis-atom-coordinates",
"source-value", "3", "1:3", "0.5", "0.0", "0.5",
"disclaimer", "This is an example disclaimer."
"key", "basis-atom-coordinates",
"source-value", "4", "2:3", "0.5", "0.5")
Arguments:
property_instances {string} -- A string containing the serialized
KIM-EDN formatted property instances.
KIM-EDN formatted property instances.
instance_id {int} -- A positive integer identifying the property
instance.
instance.
Returns:
string -- serialized KIM-EDN formatted property instances.
Expand All @@ -97,9 +105,7 @@ def kim_property_modify(property_instances, instance_id, *argv): # noqa: C901
msg = 'there is no property instance to modify the content.'
raise KIMPropertyError(msg)

if not isinstance(instance_id, int):
msg = 'the "instance_id" is not an `int`.'
raise KIMPropertyError(msg)
check_instance_id_format(instance_id)

# Deserialize the KIM property instances.
kim_property_instances = kim_edn.loads(property_instances)
Expand Down Expand Up @@ -149,6 +155,23 @@ def kim_property_modify(property_instances, instance_id, *argv): # noqa: C901
while i < n_arguments:
arg = argv[i]

if arg == 'disclaimer':
# key keyword
key = False
# new keyword
key_name = None
key_name_map = {}

if i + 1 >= n_arguments:
msg = f'there is not enough input arguments to use.\nProcessing '
msg += 'the "disclaimer" optional key-value pair failed.'
raise KIMPropertyError(msg)

i += 1
a_property_instance[arg] = argv[i]
i += 1
continue

if arg == 'key':
key = True
i += 1
Expand Down Expand Up @@ -185,6 +208,13 @@ def kim_property_modify(property_instances, instance_id, *argv): # noqa: C901
i += 1
continue

if key_name is None:
msg = f'"key name" is undefined. The input "{arg}" is a wrong '
msg += 'input or the order needs to be corrected. First, the '
msg += 'special keyword "key" should be given, followed by the '
msg += 'property "key name".'
raise KIMPropertyError(msg)

key_name_key = arg
i += 1

Expand Down
38 changes: 38 additions & 0 deletions tests/test_kim_property/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,44 @@ def test_create(self):
self.assertRaises(self.KIMPropertyError, self.kim_property.kim_property_create,
'3', 'cohesive-energy-relation-cubic-crystal')

def test_create_with_optional_keys(self):
"""Test the create functionality with optional keys."""
# Correct object
str_obj = '[{"property-id" "tag:[email protected],2014-04-15:property/' \
'cohesive-energy-relation-cubic-crystal" "instance-id" 1 ' \
'"disclaimer" "This is an example disclaimer."}]'

# Create the property instance with the property name
str1 = self.kim_property.kim_property_create(
1, 'cohesive-energy-relation-cubic-crystal',
property_disclaimer='This is an example disclaimer.')

self.assertTrue(str1 == str_obj)

str_obj2 = '[{"property-id" "tag:[email protected],2014-04-15:property/' \
'cohesive-energy-relation-cubic-crystal" "instance-id" 1 ' \
'"disclaimer" "This is an example disclaimer."} {"property-id" ' \
'"tag:[email protected],2016-05-11:property/atomic-mass" ' \
'"instance-id" 2 "disclaimer" "This is another example disclaimer."}]'

# Create the property instance with the property name to the already created instance
str3 = self.kim_property.kim_property_create(
2, 'atomic-mass', str1, 'This is another example disclaimer.')

self.assertTrue(str3 == str_obj2)

str4 = self.kim_property.kim_property_create(
2, 'atomic-mass', property_instances=str1,
property_disclaimer='This is another example disclaimer.')

self.assertTrue(str4 == str_obj2)

import kim_edn
self.assertRaises(kim_edn.decoder.KIMEDNDecodeError,
self.kim_property.kim_property_create,
2, 'atomic-mass',
'This is another example disclaimer.')

def test_create_from_a_file(self):
"""Test the create functionality for a new file as input."""
# Correct object
Expand Down
71 changes: 71 additions & 0 deletions tests/test_kim_property/test_modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,77 @@ def test_modify(self):
"key", "cohesive-potential-energy",
"si-unit", "eV-test")

def test_modify_with_optional_keys(self):
"""Test the modify functionality with optional keys."""
# Create the property instance with the property name
str_obj = self.kim_property.kim_property_create(
1, 'cohesive-energy-relation-cubic-crystal')

str_obj = self.kim_property.kim_property_modify(
str_obj, 1, "disclaimer", "This is an example disclaimer.")

kim_obj = kim_edn.load(str_obj)[0]

Property_Instance = '{"property-id" "tag:[email protected],2014-04-15:property/' \
'cohesive-energy-relation-cubic-crystal" "instance-id" 1 ' \
'"disclaimer" "This is an example disclaimer."}'

self.assertTrue(Property_Instance == kim_edn.dumps(kim_obj))

self.assertRaises(self.KIMPropertyError,
self.kim_property.kim_property_modify,
str_obj, 1,
"disclaimer")

str_obj = self.kim_property.kim_property_modify(
str_obj, 1,
"key", "short-name",
"source-value", "1", "fcc",
"disclaimer", "This is a new example disclaimer.")

kim_obj = kim_edn.load(str_obj)[0]

Property_Instance = '{"property-id" "tag:[email protected],2014-04-15:property/' \
'cohesive-energy-relation-cubic-crystal" "instance-id" 1 ' \
'"disclaimer" "This is a new example disclaimer." ' \
'"short-name" {"source-value" ["fcc"]}}'

self.assertTrue(Property_Instance == kim_edn.dumps(kim_obj))

# Create the property instance with the property name
str_obj = self.kim_property.kim_property_create(
1, 'cohesive-energy-relation-cubic-crystal')

# Create the property instance with the property name
str_obj = self.kim_property.kim_property_create(
2, 'atomic-mass', property_instances=str_obj,
property_disclaimer="This is an example disclaimer for atomic-mass.")

str_obj = self.kim_property.kim_property_modify(
str_obj, 1,
"disclaimer", "This is an example disclaimer for cohesive-energy.")

kim_obj = kim_edn.load(str_obj)

self.assertTrue(
kim_obj[0]["disclaimer"] == "This is an example disclaimer for cohesive-energy.")

self.assertTrue(
kim_obj[1]["disclaimer"] == "This is an example disclaimer for atomic-mass.")

# Create the property instance with the property name
str_obj = self.kim_property.kim_property_create(
1, 'cohesive-energy-relation-cubic-crystal')

self.assertRaises(self.KIMPropertyError,
self.kim_property.kim_property_modify,
str_obj, 1,
"key", "short-name",
"source-value", "1", "fcc",
"key", "species",
"disclaimer", "This is an example disclaimer.",
"source-value", "1:4", "Al", "Al", "Al", "Al")

def test_modify_exception(self):
"""Test the modify functionality on exceptions."""
# Fails when the input is none or not created
Expand Down

0 comments on commit d3b3a6a

Please sign in to comment.