Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.2
current_version = 0.2.0
commit = True
tag = True

Expand Down
26 changes: 26 additions & 0 deletions conceptual_dictionary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@
property_template,
workflow_template,
operation_template,
math_operation_template,
)
from conceptual_dictionary.vocabs import (
CONTROLLED_VALUES,
METHOD,
ALGORITHM,
DEGREES_OF_FREEDOM,
THERMODYNAMIC_ENSEMBLE,
POTENTIAL_TYPE,
XC_FUNCTIONAL,
OPERATION_METHOD,
GRAIN_BOUNDARY_TYPE,
MATH_OPERATION_TYPE,
YAML_TOP_LEVEL_KEYS,
)

__all__ = [
Expand All @@ -13,4 +27,16 @@
"property_template",
"workflow_template",
"operation_template",
"math_operation_template",
"CONTROLLED_VALUES",
"METHOD",
"ALGORITHM",
"DEGREES_OF_FREEDOM",
"THERMODYNAMIC_ENSEMBLE",
"POTENTIAL_TYPE",
"XC_FUNCTIONAL",
"OPERATION_METHOD",
"GRAIN_BOUNDARY_TYPE",
"MATH_OPERATION_TYPE",
"YAML_TOP_LEVEL_KEYS",
]
122 changes: 120 additions & 2 deletions conceptual_dictionary/conceptualdict.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import warnings
import yaml
import json
from typing import Any, Dict
from typing import Any, Dict, List
import numpy as np
import string
import random

from conceptual_dictionary.vocabs import CONTROLLED_VALUES


class ConceptualDict(dict):
def __init__(self, *args, **kwargs):
data = {"computational_sample": [], "workflow": [], "operation": []}
data = {
"computational_sample": [],
"workflow": [],
"operation": [],
"math_operation": [],
}
super().__init__(data, *args, **kwargs)

def generate_id(self, length=7):
Expand Down Expand Up @@ -68,3 +76,113 @@ def from_json(cls, filepath: str) -> "ConceptualDict":
kg = cls()
kg.update(data)
return kg

# --------------------
# Validation
# --------------------
def validate(self, strict: bool = False) -> List[dict]:
"""Check all entries against controlled vocabularies sourced from atomRDF.

Parameters
----------
strict : bool
If False (default), emit ``warnings.warn`` for each violation and
return the full list. If True, raise ``ValueError`` on the first
violation — useful in CI / test contexts.

Returns
-------
list[dict]
Each item has keys ``section``, ``index``, ``field``, ``value``,
``allowed`` so callers can inspect violations programmatically.
"""
violations: List[dict] = []

def _check(section: str, idx: int, field: str, value, allowed: frozenset):
if value is None:
return
values = value if isinstance(value, list) else [value]
for v in values:
if v not in allowed:
msg = (
f"[{section}][{idx}] '{field}' has invalid value '{v}'. "
f"Allowed: {sorted(allowed)}"
)
violations.append(
dict(
section=section,
index=idx,
field=field,
value=v,
allowed=allowed,
)
)
if strict:
raise ValueError(msg)
warnings.warn(msg, UserWarning, stacklevel=2)

for i, wf in enumerate(self.get("workflow", [])):
_check(
"workflow",
i,
"method",
wf.get("method"),
CONTROLLED_VALUES["workflow.method"][0],
)
_check(
"workflow",
i,
"algorithm",
wf.get("algorithm"),
CONTROLLED_VALUES["workflow.algorithm"][0],
)
_check(
"workflow",
i,
"degrees_of_freedom",
wf.get("degrees_of_freedom"),
CONTROLLED_VALUES["workflow.degrees_of_freedom"][0],
)
_check(
"workflow",
i,
"thermodynamic_ensemble",
wf.get("thermodynamic_ensemble"),
CONTROLLED_VALUES["workflow.thermodynamic_ensemble"][0],
)
_check(
"workflow",
i,
"xc_functional",
wf.get("xc_functional"),
CONTROLLED_VALUES["workflow.xc_functional"][0],
)
pot = wf.get("interatomic_potential")
if isinstance(pot, dict):
_check(
"workflow",
i,
"potential_type",
pot.get("potential_type"),
CONTROLLED_VALUES["workflow.potential_type"][0],
)

for i, op in enumerate(self.get("operation", [])):
_check(
"operation",
i,
"method",
op.get("method"),
CONTROLLED_VALUES["operation.method"][0],
)

for i, mo in enumerate(self.get("math_operation", [])):
_check(
"math_operation",
i,
"type",
mo.get("type"),
CONTROLLED_VALUES["math_operation.type"][0],
)

return violations
83 changes: 73 additions & 10 deletions conceptual_dictionary/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@
"species": None,
# Option B — file reference (preferred for large MD snapshots)
# WorkflowParser resolves this relative to the YAML file's directory.
"file_path": None, # path to structure file, e.g. '../DC3_benchmark_data_set/Al_fcc/T_0.10Tm_snapshot_1.gz'
"file_path": None, # path to structure file, e.g. '../DC3_benchmark_data_set/Al_fcc/T_0.10Tm_snapshot_1.gz'
"file_format": None, # ASE format string, e.g. 'lammps-dump-text' (auto-detected if None)
"file_species": None, # species order for LAMMPS numeric types, e.g. ['Al']
"file_species": None, # species order for LAMMPS numeric types, e.g. ['Al']
},
"calculated_property": [],
}

property_template = {
"basename": None,
"id": None, # optional local ID; used to reference this property in math_operation operands
"label": None, # primary name read by atomRDF WorkflowParser
"basename": None, # kept for backwards compat
"value": None,
"unit": None,
"associate_to_sample": [],
Expand All @@ -46,20 +48,29 @@
"algorithm": None,
"method": None,
"xc_functional": None,
"input_parameter": [],
# Each entry in these lists may include an optional 'id' field so later
# math_operation entries can reference the property by its local ID.
"input_parameter": [
# {"id": None, "label": None, "basename": None, "value": None, "unit": None}
],
"input_sample": [],
"output_sample": [],
"calculated_property": [],
"output_parameter": [
# {"id": None, "label": None, "basename": None, "value": None, "unit": None}
],
"calculated_property": [
# {"id": None, "label": None, "basename": None, "value": None, "unit": None,
# "associate_to_sample": []}
],
"degrees_of_freedom": [],
"interatomic_potential": {
"potential_type": None,
"uri": None,
},
"software": {
"uri": None,
"version": None,
"label": None,
},
# software is a list of {uri, version, label} dicts
"software": [],
# single-software template kept for reference:
# {"uri": None, "version": None, "label": None}
"workflow_manager": {
"uri": None,
"version": None,
Expand All @@ -68,6 +79,58 @@
"thermodynamic_ensemble": None,
}

dataset_template = {
# dcat:Dataset — the dataset node
"identifier": None, # URI/IRI for the dataset, e.g. "https://doi.org/10.5281/zenodo.1234567"
"title": None, # dcterms:title, e.g. "Grain boundary energies for Al"
# dcterms:creator — list of foaf:Person entries
"creators": [
{
"id": None, # URI for the person, e.g. "https://orcid.org/0000-0000-0000-0000"
"name": None, # foaf:name, e.g. "Abril Guzman"
}
],
# dcterms:isReferencedBy — the associated publication
"publication": {
"id": None, # URI for the paper
"identifier": None, # dcterms:identifier — DOI string, e.g. "10.1016/j.actamat.2024.12345"
"title": None, # dcterms:title (optional)
},
# dcterms:isPartOf — list of sample IDs that belong to this dataset
# (these are added as triples on each sample: sample dcterms:isPartOf dataset)
"samples": [],
}

# Math-operation template for ASMO arithmetic activities.
# Operands may be a local property ID string (referencing an earlier
# calculated / input / output property by its 'id' field) or a numeric
# scalar. The result is a CalculatedProperty and may carry its own 'id'
# so subsequent math_operation entries can use it as an operand.
math_operation_template = {
"type": None, # Required: Subtraction | Addition | Multiplication | Division | Exponentiation
"result": {
"id": None, # optional local ID for use as operand in later math operations
"label": None,
"basename": None,
"value": None, # set if result value is known; otherwise leave None
"unit": None,
"associate_to_sample": [],
},
# ── Subtraction ──────────────────────────────────────────────────────────
"minuend": None, # property ID or scalar
"subtrahend": None, # property ID or scalar
# ── Addition ─────────────────────────────────────────────────────────────
"addend": [], # list of property IDs and/or scalars
# ── Multiplication ───────────────────────────────────────────────────────
"factor": [], # list of property IDs and/or scalars
# ── Division ─────────────────────────────────────────────────────────────
"dividend": None, # property ID or scalar
"divisor": None, # property ID or scalar
# ── Exponentiation ───────────────────────────────────────────────────────
"base": None, # property ID or scalar
"exponent": None, # property ID or scalar
}

# Operation template for atomic-scale transformations
# Contains all possible fields for all operation types
operation_template = {
Expand Down
Loading
Loading