From 5f79e95532a92008b4a93107f20ed127c209f338 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 14:59:05 +0200 Subject: [PATCH 01/38] fix indent --- environment-workflows.yml | 2 +- pyscal_rdf/workflow.py | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/environment-workflows.yml b/environment-workflows.yml index 50a1caa..59c3573 100644 --- a/environment-workflows.yml +++ b/environment-workflows.yml @@ -1,4 +1,4 @@ -name: workflow-rdf-v0.1 +name: workflow-rdf-v0.2 channels: - conda-forge dependencies: diff --git a/pyscal_rdf/workflow.py b/pyscal_rdf/workflow.py index 69dc275..ddaaeb6 100644 --- a/pyscal_rdf/workflow.py +++ b/pyscal_rdf/workflow.py @@ -20,16 +20,16 @@ ASO = Namespace("http://purls.helmholtz-metadaten.de/aso/") def annotate_md(graph, - structure, - id = None, - pressure = None, - ensemble = None, - temperature = None, - potential_doi = None, - potential_type = "", - software_id = None, - software = None, - ): + structure, + id = None, + pressure = None, + ensemble = None, + temperature = None, + potential_doi = None, + potential_type = "", + software_id = None, + software = None, + ): """ Annotate a given structure with MD simulation details """ @@ -129,13 +129,14 @@ def annotate_md(graph, if software is not None: graph.add((software_agent, RDFS.label, Literal(software))) + def annotate_dft(graph, - structure, - id = None, - pressure = None, - software_id = None, - software = None, - ): + structure, + id = None, + pressure = None, + software_id = None, + software = None, + ): """ Annotate a given structure with MD simulation details """ From 101ef107f7462d870745cc517986f7bd35af4621 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 15:00:38 +0200 Subject: [PATCH 02/38] restructure workflow concepts --- pyscal_rdf/workflow/__init__.py | 0 pyscal_rdf/{ => workflow}/workflow.py | 1 + 2 files changed, 1 insertion(+) create mode 100644 pyscal_rdf/workflow/__init__.py rename pyscal_rdf/{ => workflow}/workflow.py (97%) diff --git a/pyscal_rdf/workflow/__init__.py b/pyscal_rdf/workflow/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyscal_rdf/workflow.py b/pyscal_rdf/workflow/workflow.py similarity index 97% rename from pyscal_rdf/workflow.py rename to pyscal_rdf/workflow/workflow.py index ddaaeb6..e643a69 100644 --- a/pyscal_rdf/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -19,6 +19,7 @@ PODO = Namespace("http://purls.helmholtz-metadaten.de/podo/") ASO = Namespace("http://purls.helmholtz-metadaten.de/aso/") + def annotate_md(graph, structure, id = None, From 236420b4fe6afda03d6eeacd61b826c79d58973e Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 15:28:33 +0200 Subject: [PATCH 03/38] add initial draft --- pyscal_rdf/workflow/pyiron.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pyscal_rdf/workflow/pyiron.py diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py new file mode 100644 index 0000000..180eeee --- /dev/null +++ b/pyscal_rdf/workflow/pyiron.py @@ -0,0 +1,11 @@ +""" +Wrappers for pyiron jobs +""" +import pyscal_rdf.workflow.workflow as wf + +def _check_if_job_is_valid(job): + valid_jobs = ['Lammps', ] + + if not type(job).__name__ in valid_jobs: + raise TypeError('These type of pyiron Job is not currently supported') + From ae6221be4ebc511cdb7caa67c62a79a3cb52948b Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 15:59:58 +0200 Subject: [PATCH 04/38] update --- pyscal_rdf/workflow/pyiron.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 180eeee..df2c6bd 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -1,7 +1,12 @@ """ Wrappers for pyiron jobs """ +import os +from functools import partial, update_wrapper import pyscal_rdf.workflow.workflow as wf +from pyscal_rdf import KnowledgeGraph, System + + def _check_if_job_is_valid(job): valid_jobs = ['Lammps', ] From a638588bbe47deb0b862747ab51f95c3e600c1b9 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:11:06 +0200 Subject: [PATCH 05/38] add annotated structure method --- pyscal_rdf/workflow/pyiron.py | 86 ++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index df2c6bd..bf425ff 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -5,7 +5,8 @@ from functools import partial, update_wrapper import pyscal_rdf.workflow.workflow as wf from pyscal_rdf import KnowledgeGraph, System - +from pyscal_rdf.structure import _make_crystal +from pyscal3.core import structure_dict, element_dict def _check_if_job_is_valid(job): @@ -14,3 +15,86 @@ def _check_if_job_is_valid(job): if not type(job).__name__ in valid_jobs: raise TypeError('These type of pyiron Job is not currently supported') + +def update_project(pr): + """ + Update project to add extra creator functions + """ + + try: + from pyiron_base import Creator, PyironFactory + from pyiron_atomistics.atomistics.structure.atoms import ase_to_pyiron, pyiron_to_ase + except ImportError: + raise ImportError('Please install pyiron_base and pyiron_atomistics') + + class StructureFactory(PyironFactory): + def __init__(self): + pass + + def bulk(self, + element, + repetitions=None, + crystalstructure=None, + a=None, + covera=None, + cubic=True, + graph=None): + + if crystalstructure is None: + crystalstructure = element_dict[element]['structure'] + if a is None: + a = _element_dict[element]['lattice_constant'] + + struct = _make_crystal(crystalstructure, + repetitions=repetitions, + lattice_constant=a, + ca_ratio = covera, + element = element, + primitive = not cubic, + graph=graph, + ) + + ase_structure = struct.write.ase() + pyiron_structure = ase_to_pyiron(ase_structure) + pyiron_structure.info['sample_id'] = struct.sample + return pyiron_structure + + + def grain_boundary(self, + element, + axis, + sigma, + gb_plane, + repetitions = (1,1,1), + crystalstructure=None, + a=1, + overlap=0.0, + graph=None, + ): + + struct = self._graph._annotated_make_grain_boundary(axis, + sigma, + gb_plane, + structure = crystalstructure, + element=element, + lattice_constant=a, + repetitions=repetitions, + overlap=overlap, + graph=graph) + + ase_structure = struct.write.ase() + pyiron_structure = ase_to_pyiron(ase_structure) + pyiron_structure.info['sample_id'] = struct.sample + return pyiron_structure + + + class StructureCreator(Creator): + def __init__(self, project): + super().__init__(project) + self._structure = StructureFactory() + + @property + def annotated_structure(self): + return self._structure + + pr._creator = StructureCreator(pr) \ No newline at end of file From 3fb06ba235626228b3c6e3abb604c0f2921ea868 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:14:45 +0200 Subject: [PATCH 06/38] fix indentation --- pyscal_rdf/workflow/pyiron.py | 74 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index bf425ff..2fad134 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -10,22 +10,22 @@ def _check_if_job_is_valid(job): - valid_jobs = ['Lammps', ] - - if not type(job).__name__ in valid_jobs: - raise TypeError('These type of pyiron Job is not currently supported') + valid_jobs = ['Lammps', ] + + if not type(job).__name__ in valid_jobs: + raise TypeError('These type of pyiron Job is not currently supported') def update_project(pr): - """ - Update project to add extra creator functions - """ + """ + Update project to add extra creator functions + """ try: - from pyiron_base import Creator, PyironFactory - from pyiron_atomistics.atomistics.structure.atoms import ase_to_pyiron, pyiron_to_ase + from pyiron_base import Creator, PyironFactory + from pyiron_atomistics.atomistics.structure.atoms import ase_to_pyiron, pyiron_to_ase except ImportError: - raise ImportError('Please install pyiron_base and pyiron_atomistics') + raise ImportError('Please install pyiron_base and pyiron_atomistics') class StructureFactory(PyironFactory): def __init__(self): @@ -40,10 +40,10 @@ def bulk(self, cubic=True, graph=None): - if crystalstructure is None: - crystalstructure = element_dict[element]['structure'] - if a is None: - a = _element_dict[element]['lattice_constant'] + if crystalstructure is None: + crystalstructure = element_dict[element]['structure'] + if a is None: + a = _element_dict[element]['lattice_constant'] struct = _make_crystal(crystalstructure, repetitions=repetitions, @@ -53,36 +53,36 @@ def bulk(self, primitive = not cubic, graph=graph, ) - - ase_structure = struct.write.ase() + + ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure - def grain_boundary(self, - element, - axis, - sigma, - gb_plane, - repetitions = (1,1,1), - crystalstructure=None, - a=1, - overlap=0.0, - graph=None, - ): + def grain_boundary(self, + element, + axis, + sigma, + gb_plane, + repetitions = (1,1,1), + crystalstructure=None, + a=1, + overlap=0.0, + graph=None, + ): - struct = self._graph._annotated_make_grain_boundary(axis, - sigma, - gb_plane, - structure = crystalstructure, - element=element, - lattice_constant=a, - repetitions=repetitions, - overlap=overlap, - graph=graph) + struct = self._graph._annotated_make_grain_boundary(axis, + sigma, + gb_plane, + structure = crystalstructure, + element=element, + lattice_constant=a, + repetitions=repetitions, + overlap=overlap, + graph=graph) - ase_structure = struct.write.ase() + ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure From 05794fed27e50526b0317014e5cc51c5502c8bf3 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:18:47 +0200 Subject: [PATCH 07/38] bug fixes --- pyscal_rdf/workflow/pyiron.py | 2 +- pyscal_rdf/workflow/workflow.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 2fad134..4b6bbf4 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -43,7 +43,7 @@ def bulk(self, if crystalstructure is None: crystalstructure = element_dict[element]['structure'] if a is None: - a = _element_dict[element]['lattice_constant'] + a = element_dict[element]['lattice_constant'] struct = _make_crystal(crystalstructure, repetitions=repetitions, diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index e643a69..af1e128 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -4,7 +4,6 @@ """ from pyscal_rdf.rdfsystem import System -from pyscal_rdf import StructureGraph from rdflib import Graph, Literal, Namespace, XSD, RDF, RDFS, BNode, URIRef, FOAF, SKOS, DCTERMS import warnings From 8e60bd5334df7457a49a6a719512e3f7ba8617e8 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:27:23 +0200 Subject: [PATCH 08/38] add store for projects --- pyscal_rdf/graph.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/graph.py b/pyscal_rdf/graph.py index bf0cbcb..bd6752f 100644 --- a/pyscal_rdf/graph.py +++ b/pyscal_rdf/graph.py @@ -109,7 +109,26 @@ def __init__(self, graph_file=None, self.graph = Graph(store="SQLAlchemy", identifier=identifier) uri = Literal(f"sqlite:///{store_file}") - self.graph.open(uri, create=True) + self.graph.open(uri, create=True) + + elif type(store).__name__ == 'Project': + self.structure_store = os.path.join(store.path, 'rdf_structure_store') + self.structure_store = _setup_structure_store(self.structure_store) + try: + import sqlalchemy as sa + except ImportError: + raise RuntimeError('Please install the sqlalchemy package') + try: + import rdflib_sqlalchemy as rsa + except ImportError: + raise RuntimeError('Please install the rdllib-sqlalchemy package. The development version is needed, please do pip install git+https://github.com/RDFLib/rdflib-sqlalchemy.git@develop') + + store_file = os.path.join(store.path, f'{store.name}.db') + self.graph = Graph(store="SQLAlchemy", identifier=identifier) + uri = Literal(f"sqlite:///{store_file}") + self.graph.open(uri, create=True) + + else: raise ValueError("Memory or SQLAlchemy") From 573bf94e76e19d2b7215e807a1a822b4613a4510 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:35:55 +0200 Subject: [PATCH 09/38] update modification of store --- pyscal_rdf/graph.py | 8 ++++++++ pyscal_rdf/workflow/__init__.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/pyscal_rdf/graph.py b/pyscal_rdf/graph.py index bd6752f..cd47c2f 100644 --- a/pyscal_rdf/graph.py +++ b/pyscal_rdf/graph.py @@ -29,6 +29,11 @@ #from pyscal3.core import System from pyscal3.atoms import Atoms + +#special methods; for supporting workflow envs +from pyscal_rdf.workflow import update_project + + CMSO = Namespace("http://purls.helmholtz-metadaten.de/cmso/") PLDO = Namespace("http://purls.helmholtz-metadaten.de/pldo/") PODO = Namespace("http://purls.helmholtz-metadaten.de/podo/") @@ -128,6 +133,9 @@ def __init__(self, graph_file=None, uri = Literal(f"sqlite:///{store_file}") self.graph.open(uri, create=True) + #finally update project object + update_project(store) + else: raise ValueError("Memory or SQLAlchemy") diff --git a/pyscal_rdf/workflow/__init__.py b/pyscal_rdf/workflow/__init__.py index e69de29..7369f13 100644 --- a/pyscal_rdf/workflow/__init__.py +++ b/pyscal_rdf/workflow/__init__.py @@ -0,0 +1,2 @@ +from pyscal_rdf.workflow.pyiron import update_project + From c502535f2c72e0ea867083a0937b2ec0f0cf3153 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 16:36:49 +0200 Subject: [PATCH 10/38] update modification of store --- pyscal_rdf/workflow/pyiron.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 4b6bbf4..b71c3f1 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -4,7 +4,6 @@ import os from functools import partial, update_wrapper import pyscal_rdf.workflow.workflow as wf -from pyscal_rdf import KnowledgeGraph, System from pyscal_rdf.structure import _make_crystal from pyscal3.core import structure_dict, element_dict From 2ecbc0468874c67d07888a5edaece781a611889c Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 17:53:47 +0200 Subject: [PATCH 11/38] generalise store creation --- pyscal_rdf/graph.py | 66 +++-------------------------------- pyscal_rdf/stores.py | 64 +++++++++++++++++++++++++++++++++ pyscal_rdf/workflow/pyiron.py | 6 ++-- 3 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 pyscal_rdf/stores.py diff --git a/pyscal_rdf/graph.py b/pyscal_rdf/graph.py index cd47c2f..74d8b34 100644 --- a/pyscal_rdf/graph.py +++ b/pyscal_rdf/graph.py @@ -5,8 +5,6 @@ """ from rdflib import Graph, Literal, Namespace, XSD, RDF, RDFS, BNode, URIRef, FOAF, SKOS, DCTERMS -from rdflib.store import NO_STORE, VALID_STORE -from rdflib import plugin import os import numpy as np @@ -26,14 +24,12 @@ from pyscal_rdf.network.ontology import read_ontology from pyscal_rdf.structure import System import pyscal_rdf.properties as prp +from pyscal_rdf.stores import create_store + #from pyscal3.core import System from pyscal3.atoms import Atoms -#special methods; for supporting workflow envs -from pyscal_rdf.workflow import update_project - - CMSO = Namespace("http://purls.helmholtz-metadaten.de/cmso/") PLDO = Namespace("http://purls.helmholtz-metadaten.de/pldo/") PODO = Namespace("http://purls.helmholtz-metadaten.de/podo/") @@ -75,12 +71,6 @@ def _replace_keys(refdict, indict): refdict[key] = val return refdict -def _setup_structure_store(structure_store): - if structure_store is None: - structure_store = os.path.join(os.getcwd(), 'rdf_structure_store') - if not os.path.exists(structure_store): - os.mkdir(structure_store) - return structure_store class KnowledgeGraph: def __init__(self, graph_file=None, @@ -90,58 +80,12 @@ def __init__(self, graph_file=None, ontology=None, structure_store=None): - self.store_file = store_file - self.structure_store = structure_store - - - if store == "Memory": - self.graph = Graph(store="Memory", identifier=identifier) - - - elif store=="SQLAlchemy": - #check for modules - try: - import sqlalchemy as sa - except ImportError: - raise RuntimeError('Please install the sqlalchemy package') - try: - import rdflib_sqlalchemy as rsa - except ImportError: - raise RuntimeError('Please install the rdllib-sqlalchemy package. The development version is needed, please do pip install git+https://github.com/RDFLib/rdflib-sqlalchemy.git@develop') - - if store_file is None: - raise ValueError("store file is needed if store is not memory") - - self.graph = Graph(store="SQLAlchemy", identifier=identifier) - uri = Literal(f"sqlite:///{store_file}") - self.graph.open(uri, create=True) - - elif type(store).__name__ == 'Project': - self.structure_store = os.path.join(store.path, 'rdf_structure_store') - self.structure_store = _setup_structure_store(self.structure_store) - try: - import sqlalchemy as sa - except ImportError: - raise RuntimeError('Please install the sqlalchemy package') - try: - import rdflib_sqlalchemy as rsa - except ImportError: - raise RuntimeError('Please install the rdllib-sqlalchemy package. The development version is needed, please do pip install git+https://github.com/RDFLib/rdflib-sqlalchemy.git@develop') - - store_file = os.path.join(store.path, f'{store.name}.db') - self.graph = Graph(store="SQLAlchemy", identifier=identifier) - uri = Literal(f"sqlite:///{store_file}") - self.graph.open(uri, create=True) - - #finally update project object - update_project(store) - - else: - raise ValueError("Memory or SQLAlchemy") + create_store(self, store, identifier, + store_file=store_file, + structure_store=structure_store) #start the storage - self.structure_store = _setup_structure_store(self.structure_store) #start binding self.graph.bind("cmso", CMSO) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py new file mode 100644 index 0000000..f94a522 --- /dev/null +++ b/pyscal_rdf/stores.py @@ -0,0 +1,64 @@ +from rdflib.store import NO_STORE, VALID_STORE +from rdflib import plugin +from rdflib import Graph + +import os +#special methods; for supporting workflow envs +from pyscal_rdf.workflow import update_project + + +def create_store(kg, store, identifier, + store_file=None, + structure_store=structure_store): + + kg.store_file = store_file + if store == 'Memory': + store_memory(kg, store, identifier, store_file=store_file, structure_store=structure_store) + elif store == 'SQLAlchemy': + store_alchemy(kg, store, identifier, store_file=store_file, structure_store=structure_store) + elif type(store).__name__ == 'Project': + store_pyiron(kg, store, identifier, store_file=store_file, structure_store=structure_store) + else: + raise ValueError('Unknown store found!') + + +def store_memory(kg, store, identifier, store_file=None, structure_store=None): + graph = Graph(store="Memory", identifier=identifier) + kg.graph = graph + kg.structure_store = _setup_structure_store(structure_store=structure_store) + +def store_alchemy(kg, store, identifier, store_file=None, structure_store=None): + _check_if_sqlalchemy_is_available() + if store_file is None: + raise ValueError("store file is needed if store is not memory") + + kg.graph = Graph(store="SQLAlchemy", identifier=identifier) + uri = Literal(f"sqlite:///{store_file}") + kg.graph.open(uri, create=True) + kg.structure_store = _setup_structure_store(structure_store=structure_store) + + +def store_pyiron(kg, store, identifier, store_file=None, structure_store=None): + structure_store = os.path.join(store.path, 'rdf_structure_store') + kg.structure_store = kg._setup_structure_store(structure_store=structure_store) + store_file = os.path.join(store.path, f'{store.name}.db') + store_alchemy(kg, store, identifier, store_file, structure_store=structure_store) + #finally update project object + update_project(store) + +def _check_if_sqlalchemy_is_available(): + try: + import sqlalchemy as sa + except ImportError: + raise RuntimeError('Please install the sqlalchemy package') + try: + import rdflib_sqlalchemy as rsa + except ImportError: + raise RuntimeError('Please install the rdllib-sqlalchemy package. The development version is needed, please do pip install git+https://github.com/RDFLib/rdflib-sqlalchemy.git@develop') + +def _setup_structure_store(structure_store=None): + if structure_store is None: + structure_store = os.path.join(os.getcwd(), 'rdf_structure_store') + if not os.path.exists(structure_store): + os.mkdir(structure_store) + return structure_store diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index b71c3f1..b3f2f7b 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -55,7 +55,8 @@ def bulk(self, ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) - pyiron_structure.info['sample_id'] = struct.sample + if graph is not None: + pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure @@ -83,7 +84,8 @@ def grain_boundary(self, ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) - pyiron_structure.info['sample_id'] = struct.sample + if graph is not None: + pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure From e831ed9e42b645e57d7deb55396c9216a168b464 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 17:56:30 +0200 Subject: [PATCH 12/38] fix bugs with store implementation --- pyscal_rdf/stores.py | 2 +- pyscal_rdf/workflow/workflow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index f94a522..d47417a 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -9,7 +9,7 @@ def create_store(kg, store, identifier, store_file=None, - structure_store=structure_store): + structure_store=None): kg.store_file = store_file if store == 'Memory': diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index af1e128..a662538 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -3,7 +3,7 @@ """ -from pyscal_rdf.rdfsystem import System +from pyscal_rdf.structure import System from rdflib import Graph, Literal, Namespace, XSD, RDF, RDFS, BNode, URIRef, FOAF, SKOS, DCTERMS import warnings From 8de566e71fa9c16da72ab8353a183d8e55c5d390 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 17:59:28 +0200 Subject: [PATCH 13/38] fix bug with project store --- pyscal_rdf/stores.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index d47417a..90167ae 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -40,7 +40,7 @@ def store_alchemy(kg, store, identifier, store_file=None, structure_store=None): def store_pyiron(kg, store, identifier, store_file=None, structure_store=None): structure_store = os.path.join(store.path, 'rdf_structure_store') - kg.structure_store = kg._setup_structure_store(structure_store=structure_store) + kg.structure_store = _setup_structure_store(structure_store=structure_store) store_file = os.path.join(store.path, f'{store.name}.db') store_alchemy(kg, store, identifier, store_file, structure_store=structure_store) #finally update project object From e0e4d8fccb27311179bb0b08cd228f3e25b9ffff Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 18:00:00 +0200 Subject: [PATCH 14/38] fix bug with project store --- pyscal_rdf/stores.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index 90167ae..014c420 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -1,6 +1,6 @@ from rdflib.store import NO_STORE, VALID_STORE from rdflib import plugin -from rdflib import Graph +from rdflib import Graph, Literal import os #special methods; for supporting workflow envs From 64a95cefb5ef81074aa3024da0e677aa60d7c7b4 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 18:03:02 +0200 Subject: [PATCH 15/38] store graph in project --- pyscal_rdf/stores.py | 2 +- pyscal_rdf/workflow/pyiron.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index 014c420..46bb386 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -44,7 +44,7 @@ def store_pyiron(kg, store, identifier, store_file=None, structure_store=None): store_file = os.path.join(store.path, f'{store.name}.db') store_alchemy(kg, store, identifier, store_file, structure_store=structure_store) #finally update project object - update_project(store) + update_project(store, kg) def _check_if_sqlalchemy_is_available(): try: diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index b3f2f7b..c7bc9d0 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -15,7 +15,7 @@ def _check_if_job_is_valid(job): raise TypeError('These type of pyiron Job is not currently supported') -def update_project(pr): +def update_project(pr, kg): """ Update project to add extra creator functions """ @@ -98,4 +98,5 @@ def __init__(self, project): def annotated_structure(self): return self._structure - pr._creator = StructureCreator(pr) \ No newline at end of file + pr._creator = StructureCreator(pr) + pr.graph = kg \ No newline at end of file From bf201c69786ec09a4ae88d08025e9155c3bc3021 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 18:05:19 +0200 Subject: [PATCH 16/38] store graph in creator --- pyscal_rdf/workflow/pyiron.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index c7bc9d0..938c00a 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -27,8 +27,8 @@ def update_project(pr, kg): raise ImportError('Please install pyiron_base and pyiron_atomistics') class StructureFactory(PyironFactory): - def __init__(self): - pass + def __init__(self, graph): + self.graph = graph def bulk(self, element, @@ -36,8 +36,7 @@ def bulk(self, crystalstructure=None, a=None, covera=None, - cubic=True, - graph=None): + cubic=True): if crystalstructure is None: crystalstructure = element_dict[element]['structure'] @@ -50,7 +49,7 @@ def bulk(self, ca_ratio = covera, element = element, primitive = not cubic, - graph=graph, + graph=self.graph, ) ase_structure = struct.write.ase() @@ -80,7 +79,7 @@ def grain_boundary(self, lattice_constant=a, repetitions=repetitions, overlap=overlap, - graph=graph) + graph=self.graph) ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) @@ -92,7 +91,7 @@ def grain_boundary(self, class StructureCreator(Creator): def __init__(self, project): super().__init__(project) - self._structure = StructureFactory() + self._structure = StructureFactory(project.graph) @property def annotated_structure(self): From 365d4311cbccb53b3f12e1f911718034c5098903 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 18:06:09 +0200 Subject: [PATCH 17/38] change call order --- pyscal_rdf/workflow/pyiron.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 938c00a..47a401f 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -97,5 +97,6 @@ def __init__(self, project): def annotated_structure(self): return self._structure + pr.graph = kg pr._creator = StructureCreator(pr) - pr.graph = kg \ No newline at end of file + \ No newline at end of file From 93247e4a39ee5c75c19a6f5834906067290b0528 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 18:07:08 +0200 Subject: [PATCH 18/38] always force graph storage --- pyscal_rdf/workflow/pyiron.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 47a401f..aa90e7c 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -54,8 +54,7 @@ def bulk(self, ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) - if graph is not None: - pyiron_structure.info['sample_id'] = struct.sample + pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure @@ -83,8 +82,7 @@ def grain_boundary(self, ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) - if graph is not None: - pyiron_structure.info['sample_id'] = struct.sample + pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure From 3b34a11bcd88dbb95a04fb0b9f4089af35ac879d Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 20:41:27 +0200 Subject: [PATCH 19/38] update structure factory such that original structure is preserved --- pyscal_rdf/workflow/pyiron.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index aa90e7c..a13d60f 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -23,20 +23,22 @@ def update_project(pr, kg): try: from pyiron_base import Creator, PyironFactory from pyiron_atomistics.atomistics.structure.atoms import ase_to_pyiron, pyiron_to_ase + import pyiron_atomistics.atomistics.structure.factory as sf except ImportError: raise ImportError('Please install pyiron_base and pyiron_atomistics') - class StructureFactory(PyironFactory): + class AnnotatedStructureFactory: def __init__(self, graph): - self.graph = graph - + self._graph = graph + def bulk(self, element, repetitions=None, crystalstructure=None, a=None, covera=None, - cubic=True): + cubic=True, + graph=None): if crystalstructure is None: crystalstructure = element_dict[element]['structure'] @@ -49,7 +51,7 @@ def bulk(self, ca_ratio = covera, element = element, primitive = not cubic, - graph=self.graph, + graph=self._graph, ) ase_structure = struct.write.ase() @@ -57,7 +59,6 @@ def bulk(self, pyiron_structure.info['sample_id'] = struct.sample return pyiron_structure - def grain_boundary(self, element, axis, @@ -78,7 +79,7 @@ def grain_boundary(self, lattice_constant=a, repetitions=repetitions, overlap=overlap, - graph=self.graph) + graph=self._graph) ase_structure = struct.write.ase() pyiron_structure = ase_to_pyiron(ase_structure) @@ -86,13 +87,23 @@ def grain_boundary(self, return pyiron_structure + class StructureFactory(sf.StructureFactory): + def __init__(self, graph): + super().__init__() + self._annotated_structure = AnnotatedStructureFactory(graph) + + @property + def annotated_structure(self): + return self._annotated_structure + + class StructureCreator(Creator): def __init__(self, project): super().__init__(project) self._structure = StructureFactory(project.graph) - + @property - def annotated_structure(self): + def structure(self): return self._structure pr.graph = kg From 773a4e5ceb62cec7b37a88113fbbe68c2790ebf5 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 20:47:32 +0200 Subject: [PATCH 20/38] add method on graph to add structure --- pyscal_rdf/graph.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/graph.py b/pyscal_rdf/graph.py index 74d8b34..1f7864a 100644 --- a/pyscal_rdf/graph.py +++ b/pyscal_rdf/graph.py @@ -106,7 +106,11 @@ def __init__(self, graph_file=None, self._atom_ids = None self.store = store - + + def add_structure(self, structure): + structure.graph = self + structure.to_graph() + def add(self, triple): if str(triple[2].toPython()) != 'None': self.graph.add(triple) From dc6b443e0a9f1b1a9e786536d4c04052df30d28b Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 20:53:03 +0200 Subject: [PATCH 21/38] add method for adding structures from jobs --- pyscal_rdf/workflow/pyiron.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index a13d60f..654d7e7 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -5,6 +5,7 @@ from functools import partial, update_wrapper import pyscal_rdf.workflow.workflow as wf from pyscal_rdf.structure import _make_crystal +from pyscal_rdf.structure import System from pyscal3.core import structure_dict, element_dict @@ -15,6 +16,15 @@ def _check_if_job_is_valid(job): raise TypeError('These type of pyiron Job is not currently supported') +def _add_structures(kg, job): + initial_structure = job.structure + final_structure = job.get_structure(frame=-1) + if 'sample_id' in initial_structure.info.keys(): + if not initial_structure.info['sample_id'] in kg.samples: + kg.add_structure(System.read.ase(initial_structure)) + else: + kg.add_structure(System.read.ase(initial_structure)) + def update_project(pr, kg): """ Update project to add extra creator functions From 73a8c9d29f46f26df6dc5cf8cf4cf2f1460efe46 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 21:37:16 +0200 Subject: [PATCH 22/38] add methods to transfer inherited properties --- pyscal_rdf/structure.py | 2 +- pyscal_rdf/workflow/workflow.py | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/structure.py b/pyscal_rdf/structure.py index d94f721..6aa02c5 100644 --- a/pyscal_rdf/structure.py +++ b/pyscal_rdf/structure.py @@ -543,7 +543,7 @@ def _add_lattice_properties(self): """ data = self.schema.material.crystal_structure.unit_cell.lattice_parameter() lattice_parameter = URIRef(f'{self._name}_LatticeParameter') - self.graph.add((self.unit_cell, CMSO.hasLatticeParamter, lattice_parameter)) + self.graph.add((self.unit_cell, CMSO.hasLatticeParameter, lattice_parameter)) self.graph.add((lattice_parameter, RDF.type, CMSO.LatticeParameter)) self.graph.add((lattice_parameter, CMSO.hasLength_x, Literal(data[0], datatype=XSD.float))) self.graph.add((lattice_parameter, CMSO.hasLength_y, Literal(data[1], datatype=XSD.float))) diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index a662538..e77d108 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -184,3 +184,62 @@ def annotate_dft(graph, if software is not None: graph.add((software_agent, RDFS.label, Literal(software))) + +def _get_inherited_properties(kg, from_sample, to_sample): + #Here we need to add inherited info: CalculatedProperties will be lost + #Defects will be inherited + #add vac stuff + material = list([k[2] for k in kg.graph.triples((from_sample, CMSO.hasMaterial, None))])[0] + defects = list([x[2] for x in kg.graph.triples((material, CMSO.hasDefect, None))]) + #now for each defect we copy add this to the final sample + final_material = list([k[2] for k in kg.graph.triples((to_sample, CMSO.hasMaterial, None))])[0] + + for defect in defects: + new_defect = URIRef(defect.toPython()) + kg.graph.add((final_material, CMSO.hasDefect, new_defect)) + #now fetch all defect based info + for triple in kg.graph.triples((defect, None, None)): + kg.graph.add((new_defect, triple[1], triple[2])) + + #now add the special props for vacancy + initial_simcell = kg.graph.value(from_sample, CMSO.hasSimulationCell) + final_simcell = kg.graph.value(to_sample, CMSO.hasSimulationCell) + for triple in kg.graph.triples((initial_simcell, PODO.hasVacancyConcentration, None)): + kg.graph.add((final_simcell, triple[1], triple[2])) + for triple in kg.graph.triples((initial_simcell, PODO.hasNumberOfVacancies, None)): + kg.graph.add((final_simcell, triple[1], triple[2])) + +def _get_lattice_properties(kg, from_sample, to_sample): + material = list([k[2] for k in kg.graph.triples((from_sample, CMSO.hasMaterial, None))])[0] + crystal_structure = kg.graph.value(material, CMSO.hasStructure) + altname = kg.graph.value(crystalstructure, CMSO.hasAltName) + + #add this to new structure + final_material = list([k[2] for k in kg.graph.triples((to_sample, CMSO.hasMaterial, None))])[0] + final_crystal_structure = kg.graph.value(final_material, CMSO.hasStructure) + kg.add((final_crystal_structure, CMSO.hasAltName, altname)) + + #space group + space_group = kg.graph.value(crystal_structure, CMSO.hasSpaceGroup) + final_space_group = kg.graph.value(final_crystal_structure, CMSO.hasSpaceGroup) + for triple in kg.graph.triples((space_group, None, None)): + kg.graph.add((final_space_group, triple[1], triple[2])) + + #unit cell + unit_cell = kg.graph.value(crystal_structure, CMSO.hasUnitCell) + bv = kg.graph.value(unit_cell, CMSO.hasBravaisLattice) + + final_unit_cell = kg.graph.value(final_crystal_structure, CMSO.hasUnitCell) + kg.graph.add((final_unit_cell, CMSO.hasBravaisLattice, bv)) + + #lattice parameter + lattice_parameter = kg.graph.value(unit_cell, CMSO.hasLatticeParameter) + final_lattice_parameter = kg.graph.value(final_unit_cell, CMSO.hasLatticeParameter) + for triple in kg.graph.triples((lattice_parameter, None, None)): + kg.graph.add((final_lattice_parameter, triple[1], triple[2])) + + #lattice angle + lattice_angle = kg.graph.value(unit_cell, CMSO.hasAngle) + final_lattice_angle = kg.graph.value(final_unit_cell, CMSO.hasAngle) + for triple in kg.graph.triples((lattice_angle, None, None)): + kg.graph.add((final_lattice_angle, triple[1], triple[2])) \ No newline at end of file From c7575c5bf2e0130ae656bd779f2dafec3e04bf5d Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 21:46:25 +0200 Subject: [PATCH 23/38] update structure transfer method --- pyscal_rdf/workflow/pyiron.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 654d7e7..6e6fab5 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -17,13 +17,29 @@ def _check_if_job_is_valid(job): def _add_structures(kg, job): - initial_structure = job.structure - final_structure = job.get_structure(frame=-1) + initial_pyiron_structure = job.structure + final_pyiron_structure = job.get_structure(frame=-1) + initial_pyscal_structure = System.read.ase(initial_pyiron_structure) + + initial_sample_id = None if 'sample_id' in initial_structure.info.keys(): - if not initial_structure.info['sample_id'] in kg.samples: - kg.add_structure(System.read.ase(initial_structure)) + if not initial_pyiron_structure.info['sample_id'] in kg.samples: + kg.add_structure(initial_pyscal_structure) + else: + initial_sample_id = initial_pyiron_structure.info['sample_id'] else: - kg.add_structure(System.read.ase(initial_structure)) + kg.add_structure(initial_pyscal_structure) + + if initial_sample_id is None: + initial_sample_id = initial_pyscal_structure.sample + + #add final structure + final_pyscal_structure = System.read.ase(final_structure, graph=kg) + final_sample_id = final_pyscal_structure.sample + + #now we do rthe transfer + wf._get_inherited_properties(kg, initial_sample_id, final_sample_id) + wf._get_lattice_properties(kg, initial_sample_id, final_sample_id) def update_project(pr, kg): """ From 4bb5b850e0a03ae0d226d9921a5c4d4f5757442d Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 21:53:51 +0200 Subject: [PATCH 24/38] update wf structure transfer --- pyscal_rdf/workflow/pyiron.py | 7 +++---- pyscal_rdf/workflow/workflow.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 6e6fab5..9975f07 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -22,7 +22,7 @@ def _add_structures(kg, job): initial_pyscal_structure = System.read.ase(initial_pyiron_structure) initial_sample_id = None - if 'sample_id' in initial_structure.info.keys(): + if 'sample_id' in initial_pyiron_structure.info.keys(): if not initial_pyiron_structure.info['sample_id'] in kg.samples: kg.add_structure(initial_pyscal_structure) else: @@ -34,12 +34,11 @@ def _add_structures(kg, job): initial_sample_id = initial_pyscal_structure.sample #add final structure - final_pyscal_structure = System.read.ase(final_structure, graph=kg) + final_pyscal_structure = System.read.ase(final_pyiron_structure, graph=kg) final_sample_id = final_pyscal_structure.sample #now we do rthe transfer - wf._get_inherited_properties(kg, initial_sample_id, final_sample_id) - wf._get_lattice_properties(kg, initial_sample_id, final_sample_id) + wf.add_derived_structure(kg, initial_sample_id, final_sample_id) def update_project(pr, kg): """ diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index e77d108..39873bd 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -212,7 +212,7 @@ def _get_inherited_properties(kg, from_sample, to_sample): def _get_lattice_properties(kg, from_sample, to_sample): material = list([k[2] for k in kg.graph.triples((from_sample, CMSO.hasMaterial, None))])[0] crystal_structure = kg.graph.value(material, CMSO.hasStructure) - altname = kg.graph.value(crystalstructure, CMSO.hasAltName) + altname = kg.graph.value(crystal_structure, CMSO.hasAltName) #add this to new structure final_material = list([k[2] for k in kg.graph.triples((to_sample, CMSO.hasMaterial, None))])[0] @@ -242,4 +242,13 @@ def _get_lattice_properties(kg, from_sample, to_sample): lattice_angle = kg.graph.value(unit_cell, CMSO.hasAngle) final_lattice_angle = kg.graph.value(final_unit_cell, CMSO.hasAngle) for triple in kg.graph.triples((lattice_angle, None, None)): - kg.graph.add((final_lattice_angle, triple[1], triple[2])) \ No newline at end of file + kg.graph.add((final_lattice_angle, triple[1], triple[2])) + + +def add_derived_structure(kg, initial_sample, final_sample): + _get_inherited_properties(kg, initial_sample, final_sample) + _get_lattice_properties(kg, initial_sample, final_sample) + + kg.add((initial_sample, RDF.type, PROV.Entity)) + kg.add((final_sample, RDF.type, PROV.Entity)) + kg.add((final_sample, PROV.wasDerivedFrom, initial_sample)) From ec3534725459734eade71378da13bf99869f05b4 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 22:02:21 +0200 Subject: [PATCH 25/38] change name defs --- pyscal_rdf/structure.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyscal_rdf/structure.py b/pyscal_rdf/structure.py index 6aa02c5..ce501cb 100644 --- a/pyscal_rdf/structure.py +++ b/pyscal_rdf/structure.py @@ -42,7 +42,7 @@ def _make_crystal(structure, element=None, primitive=False, graph=None, - names=True): + names=False): atoms, box, sdict = pcs.make_crystal(structure, lattice_constant=lattice_constant, @@ -70,7 +70,7 @@ def _make_general_lattice(positions, noise = 0, element=None, graph=None, - names=True): + names=False): atoms, box, sdict = pcs.general_lattice(positions, types, @@ -98,7 +98,7 @@ def _make_grain_boundary(axis, repetitions = (1,1,1), overlap=0.0, graph=None, - names=True): + names=False): gb = GrainBoundary() gb.create_grain_boundary(axis=axis, sigma=sigma, @@ -194,7 +194,7 @@ def __init__(self, filename = None, species = None, source=None, graph=None, - names=True): + names=False): super().__init__(filename = filename, format = format, From 1c8f3034dc40975963ae6ebc185ec41cc6ae3d47 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 22:49:14 +0200 Subject: [PATCH 26/38] add job identification method --- pyscal_rdf/workflow/pyiron.py | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 9975f07..e4cb90e 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -40,6 +40,45 @@ def _add_structures(kg, job): #now we do rthe transfer wf.add_derived_structure(kg, initial_sample_id, final_sample_id) + +def _identify_method(job): + job_dict = job.input.to_dict() + input_dict = {job_dict['control_inp/data_dict']['Parameter'][x]:job_dict['control_inp/data_dict']['Value'][x] for x in range(len(job_dict['control_inp/data_dict']['Parameter']))} + method = None + temp = None + press = None + + if 'min_style' in input_dict.keys(): + method = 'minimization' + + elif 'nve' in input_dict['fix___ensemble']: + if int(input_dict['run']) == 0: + method = 'static' + elif int(input_dict['run']) > 0: + method = 'md_nve' + + elif 'nvt' in input_dict['fix___ensemble']: + method = 'md_nvt' + raw = input_dict['fix___ensemble'].split() + temp = float(raw[3]) + + elif 'npt' in input_dict['fix___ensemble']: + if 'aniso' in input_dict['fix___ensemble']: + method = 'md_npt_aniso' + else: + method = 'md_npt_iso' + raw = input_dict['fix___ensemble'].split() + temp = float(raw[3]) + press = float(raw[7]) + + + mdict = {} + mdict['method'] = method + mdict['temperature'] = temp + mdict['pressure'] = press + + return mdict + def update_project(pr, kg): """ Update project to add extra creator functions From e374dda14552ad58c9c35daed2da4cd0565493e3 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 23:07:34 +0200 Subject: [PATCH 27/38] add more features to job identification method --- pyscal_rdf/workflow/pyiron.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index e4cb90e..ea695fa 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -44,38 +44,59 @@ def _add_structures(kg, job): def _identify_method(job): job_dict = job.input.to_dict() input_dict = {job_dict['control_inp/data_dict']['Parameter'][x]:job_dict['control_inp/data_dict']['Value'][x] for x in range(len(job_dict['control_inp/data_dict']['Parameter']))} - method = None + dof = [] temp = None press = None + md_method = None + ensemble = None if 'min_style' in input_dict.keys(): - method = 'minimization' + dof.append('AtomicPosition') + dof.append('CellVolume') + md_method = 'MolecularStatics' elif 'nve' in input_dict['fix___ensemble']: if int(input_dict['run']) == 0: method = 'static' + md_method = 'MolecularStatics' + ensemble = 'NVE' + elif int(input_dict['run']) > 0: method = 'md_nve' + dof.append('AtomicPosition') + md_method = 'MolecularDynamics' + ensemble = 'NVE' + elif 'nvt' in input_dict['fix___ensemble']: method = 'md_nvt' raw = input_dict['fix___ensemble'].split() temp = float(raw[3]) + dof.append('AtomicPosition') + md_method = 'MolecularDynamics' + ensemble = 'NVT' elif 'npt' in input_dict['fix___ensemble']: + dof.append('AtomicPosition') + dof.append('CellVolume') if 'aniso' in input_dict['fix___ensemble']: method = 'md_npt_aniso' + dof.append('CellShape') else: method = 'md_npt_iso' + md_method = 'MolecularDynamics' raw = input_dict['fix___ensemble'].split() temp = float(raw[3]) press = float(raw[7]) + ensemble = 'NPT' mdict = {} - mdict['method'] = method + mdict['method'] = md_method mdict['temperature'] = temp mdict['pressure'] = press + mdict['dof'] = dof + mdict['ensemble'] = ensemble return mdict From 2f11178fd618dee2bc3ae4b939de52459e2355d4 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 23:31:02 +0200 Subject: [PATCH 28/38] add method triples to graph --- pyscal_rdf/workflow/pyiron.py | 2 ++ pyscal_rdf/workflow/workflow.py | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index ea695fa..a392e50 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -97,9 +97,11 @@ def _identify_method(job): mdict['pressure'] = press mdict['dof'] = dof mdict['ensemble'] = ensemble + mdict['id'] = job.id return mdict + def update_project(pr, kg): """ Update project to add extra creator functions diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 39873bd..61e1b12 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -252,3 +252,44 @@ def add_derived_structure(kg, initial_sample, final_sample): kg.add((initial_sample, RDF.type, PROV.Entity)) kg.add((final_sample, RDF.type, PROV.Entity)) kg.add((final_sample, PROV.wasDerivedFrom, initial_sample)) + +def add_method(kg, mdict): + main_id = mdict['id'] + activity = URIRef(f'activity:{main_id}') + kg.add((activity, RDF.type, PROV.Activity)) + + if len(mdict['dof']) == 0: + kg.add((activity, RDF.type, ASO.RigidEnergyCalculation)) + else: + kg.add((activity, RDF.type, ASO.StructureOptimization)) + + method = URIRef(f'method:{main_id}') + if mdict['method'] == 'MolecularStatics': + kg.add((method, RDF.type, ASO.MolecularStatics)) + elif mdict['method'] == 'MolecularDynamics': + kg.add((method, RDF.type, ASO.MolecularDynamics)) + kg.add((activity, ASO.hasMethod, method)) + + for dof in mdict['dof']: + kg.add((activity, ASO.hasRelaxationDOF, getattr(ASO, dof))) + + kg.add((method, ASO.hasStatisticalEnsemble, getattr(ASO, mdict['ensemble']))) + + + #add temperature if needed + if mdict['temperature'] is not None: + temperature = URIRef(f'temperature:{main_id}') + kg.add((temperature, RDF.type, ASO.InputParameter)) + kg.add((temperature, RDFS.label, Literal('temperature', datatype=XSD.string))) + kg.add((activity, ASO.hasInputParameter, temperature)) + kg.add((temperature, ASO.hasValue, Literal(mdict['temperature'], datatype=XSD.float))) + kg.add((temperature, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/K'))) + + if mdict['pressure'] is not None: + pressure = URIRef(f'pressure:{main_id}') + kg.add((pressure, RDF.type, ASO.InputParameter)) + kg.add((pressure, RDFS.label, Literal('pressure', datatype=XSD.string))) + kg.add((activity, ASO.hasInputParameter, pressure)) + kg.add((pressure, ASO.hasValue, Literal(mdict['pressure'], datatype=XSD.float))) + kg.add((pressure, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/GigaPA'))) + From 4a069116aabc957e7dd6a6380a0202aa4743e6f9 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Tue, 9 Apr 2024 23:33:16 +0200 Subject: [PATCH 29/38] add wrapper function --- pyscal_rdf/workflow/pyiron.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index a392e50..7b32738 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -102,6 +102,10 @@ def _identify_method(job): return mdict +def _add_method(kg, job): + mdict = _identify_method(job) + wf.add_method(kg, mdict) + def update_project(pr, kg): """ Update project to add extra creator functions From 55006800385bbf557ec5fc1dfe26ca61886d8c31 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Wed, 10 Apr 2024 11:03:48 +0200 Subject: [PATCH 30/38] update workflow class draft --- pyscal_rdf/workflow/pyiron.py | 13 +- pyscal_rdf/workflow/workflow.py | 489 ++++++++++++++------------------ 2 files changed, 230 insertions(+), 272 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 7b32738..4034a3f 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -38,7 +38,7 @@ def _add_structures(kg, job): final_sample_id = final_pyscal_structure.sample #now we do rthe transfer - wf.add_derived_structure(kg, initial_sample_id, final_sample_id) + return final_sample_id def _identify_method(job): @@ -104,7 +104,16 @@ def _identify_method(job): def _add_method(kg, job): mdict = _identify_method(job) - wf.add_method(kg, mdict) + activity_id = wf.add_method(kg, mdict) + return activity_id + +def add_mappings(kg, job): + final_sample_id = _add_structures(kg, job) + final_sample_id = wf.add_derived_structure(kg, initial_sample_id, final_sample_id) + + activity_id = _add_method(kg, job) + + def update_project(pr, kg): """ diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 61e1b12..ceea86f 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -18,278 +18,227 @@ PODO = Namespace("http://purls.helmholtz-metadaten.de/podo/") ASO = Namespace("http://purls.helmholtz-metadaten.de/aso/") - -def annotate_md(graph, - structure, - id = None, - pressure = None, - ensemble = None, - temperature = None, - potential_doi = None, - potential_type = "", - software_id = None, - software = None, - ): - """ - Annotate a given structure with MD simulation details - """ - main_id = str(uuid.uuid4()) - if id is not None: - main_id = id +class Workflow: + def __init__(self, kg, + structure=None, sample=None, + parent_structure=None, parent_sample=None, + method_dict=None): + self.kg = kg + + if (structure is None) and (sample is None): + raise ValueError('Either structure or sample should be specified') + + if sample is None: + #its not added to graph yet + structure.graph = kg + structure.to_graph() + sample = structure.sample - sample = structure.sample - graph.add((sample, RDF.type, PROV.Entity)) - - activity = URIRef(f'activity:{main_id}') - graph.add((activity, RDF.type, PROV.Activity)) - graph.add((activity, RDF.type, ASO.StructureOptimization)) - - method = URIRef(f'method:{main_id}') - graph.add((method, RDF.type, ASO.MolecularDynamics)) - graph.add((activity, ASO.hasMethod, method)) - graph.add((activity, ASO.hasRelaxationDOF, ASO.AtomicPosition)) - - if pressure is None: - pass - elif np.isscalar(pressure): - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) - else: - #check if pressure is hydrostatic or not - axial_all_alike = None not in pressure[:3] and np.allclose( - pressure[:3], pressure[0] - ) - shear_all_none = all(p is None for p in pressure[3:]) - shear_all_zero = None not in pressure[3:] and np.allclose(pressure[3:], 0) - hydrostatic = axial_all_alike and (shear_all_none or shear_all_zero) - if hydrostatic: - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) - else: - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellShape)) - - if ensemble is not None: - if ensemble == 'NVT': - graph.add((method, ASO.hasStatisticalEnsemble, ASO.NVT)) - if temperature is None: - raise ValueError('Temperature cannot be None in NVT') - temperature_node = URIRef(f'temperature:{main_id}') - graph.add((temperature_node, RDF.type, ASO.InputParameter)) - graph.add((temperature_node, RDFS.label, Literal('temperature', datatype=XSD.string))) - graph.add((activity, ASO.hasInputParameter, temperature_node)) - graph.add((temperature_node, ASO.hasValue, Literal(temperature, datatype=XSD.float))) - graph.add((temperature_node, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/K'))) - - elif ensemble == 'NPT': - graph.add((method, ASO.hasStatisticalEnsemble, ASO.NPT)) - if temperature is None: - raise ValueError('Temperature cannot be None in NPT') - temperature_node = URIRef(f'temperature:{main_id}') - graph.add((temperature_node, RDF.type, ASO.InputParameter)) - graph.add((temperature_node, RDFS.label, Literal('temperature', datatype=XSD.string))) - graph.add((activity, ASO.hasInputParameter, temperature_node)) - graph.add((temperature_node, ASO.hasValue, Literal(temperature, datatype=XSD.float))) - graph.add((temperature_node, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/K'))) - - pressure_node = URIRef(f'pressure:{main_id}') - graph.add((pressure_node, RDF.type, ASO.InputParameter)) - graph.add((pressure_node, RDFS.label, Literal('pressure', datatype=XSD.string))) - graph.add((activity, ASO.hasInputParameter, pressure_node)) - graph.add((pressure_node, ASO.hasValue, Literal(pressure, datatype=XSD.float))) - graph.add((pressure_node, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/GigaPA'))) - - graph.add((sample, PROV.wasGeneratedBy, activity)) - - if potential_doi is None: - warnings.warn('potential_doi is None, maybe consider providing it?') - else: - potential = URIRef(f'potential:{main_id}') - - if 'meam' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.MEAM)) - elif 'eam' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.EAM)) - elif 'lj' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.LennardJones)) - elif 'ace' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.MLPotential)) - elif 'snap' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.MLPotential)) - elif 'tersoff' in potential_type.lower(): - graph.add((potential, RDF.type, ASO.Tersoff)) + if parent_sample is None: + #its not added to graph yet + parent_structure.graph = kg + parent_structure.to_graph() + parent_sample = parent_structure.sample + + self.mdict = method_dict + self.parent_sample = parent_sample + self.add_structural_relation() + self.add_method() + + def _add_inherited_properties(self, ): + #Here we need to add inherited info: CalculatedProperties will be lost + #Defects will be inherited + if self.parent_sample is None: + return + + parent_material = list([k[2] for k in self.kg.graph.triples((self.parent_sample, CMSO.hasMaterial, None))])[0] + parent_defects = list([x[2] for x in self.kg.graph.triples((parent_material, CMSO.hasDefect, None))]) + #now for each defect we copy add this to the final sample + material = list([k[2] for k in self.kg.graph.triples((self.sample, CMSO.hasMaterial, None))])[0] + + for defect in defects: + new_defect = URIRef(defect.toPython()) + self.kg.graph.add((material, CMSO.hasDefect, new_defect)) + #now fetch all defect based info + for triple in self.kg.graph.triples((defect, None, None)): + self.kg.graph.add((new_defect, triple[1], triple[2])) + + #now add the special props for vacancy + parent_simcell = kg.graph.value(self.sample, CMSO.hasSimulationCell) + simcell = kg.graph.value(self.parent_sample, CMSO.hasSimulationCell) + + for triple in self.kg.graph.triples((parent_simcell, PODO.hasVacancyConcentration, None)): + self.kg.graph.add((simcell, triple[1], triple[2])) + for triple in self.kg.graph.triples((parent_simcell, PODO.hasNumberOfVacancies, None)): + self.kg.graph.add((simcell, triple[1], triple[2])) + + def _get_lattice_properties(self, ): + if self.parent_sample is None: + return + + parent_material = list([k[2] for k in self.kg.graph.triples((self.parent_sample, CMSO.hasMaterial, None))])[0] + parent_crystal_structure = self.kg.graph.value(parent_material, CMSO.hasStructure) + parent_altname = self.kg.graph.value(parent_crystal_structure, CMSO.hasAltName) + + #add this to new structure + material = list([k[2] for k in self.kg.graph.triples((self.sample, CMSO.hasMaterial, None))])[0] + crystal_structure = self.kg.graph.value(material, CMSO.hasStructure) + self.kg.add((crystal_structure, CMSO.hasAltName, parent_altname)) + + #space group + parent_space_group = self.kg.graph.value(parent_crystal_structure, CMSO.hasSpaceGroup) + space_group = self.kg.graph.value(crystal_structure, CMSO.hasSpaceGroup) + for triple in self.kg.graph.triples((parent_space_group, None, None)): + self.kg.graph.add((space_group, triple[1], triple[2])) + + #unit cell + parent_unit_cell = self.kg.graph.value(parent_crystal_structure, CMSO.hasUnitCell) + parent_bv = self.kg.graph.value(parent_unit_cell, CMSO.hasBravaisLattice) + + unit_cell = self.kg.graph.value(crystal_structure, CMSO.hasUnitCell) + self.kg.graph.add((unit_cell, CMSO.hasBravaisLattice, parent_bv)) + + #lattice parameter + parent_lattice_parameter = self.kg.graph.value(parent_unit_cell, CMSO.hasLatticeParameter) + lattice_parameter = self.kg.graph.value(unit_cell, CMSO.hasLatticeParameter) + for triple in self.kg.graph.triples((parent_lattice_parameter, None, None)): + self.kg.graph.add((lattice_parameter, triple[1], triple[2])) + + #lattice angle + parent_lattice_angle = self.kg.graph.value(parent_unit_cell, CMSO.hasAngle) + lattice_angle = self.kg.graph.value(unit_cell, CMSO.hasAngle) + for triple in self.kg.graph.triples((parent_lattice_angle, None, None)): + self.kg.graph.add((lattice_angle, triple[1], triple[2])) + + + def add_structural_relation(self, ): + self.kg.add((self.sample, RDF.type, PROV.Entity)) + if self.parent_sample is not None: + self.kg.add((self.parent_sample, RDF.type, PROV.Entity)) + self.kg.add((self.sample, PROV.wasDerivedFrom, self.parent_sample)) + self._add_inherited_properties() + self._get_lattice_properties() + + + def add_method(self, ): + """ + mdict + ----- + md: + method: MolecularStatics + temperature: 100 + pressure: 0 + dof: + - AtomicPositions + - CellVolume + ensemble: NPT + id: 2314 + potential: + uri: https://doi.org/xxx + type: eam + label: string + workflow_manager: + uri: xxxx + label: pyiron + software: + - uri: xxxx + label: lammps + - uri: xxxx + label: pyscal + + """ + if self.mdict is None: + return + + if 'md' in self.mdict.keys(): + method_type = 'md' + mdict = self.mdict['md'] + elif 'dft' in self.mdict.keys(): + method_type = 'dft' + mdict = self.mdict['dft'] else: - graph.add((potential, RDF.type, ASO.InteratomicPotential)) - - graph.add((potential, ASO.hasReference, Literal(potential_doi))) - graph.add((method, ASO.hasInteratomicPotential, potential)) - - if software_id is not None: - software_agent = URIRef(software_id) - graph.add((software_agent, RDF.type, PROV.SoftwareAgent)) - graph.add((activity, PROV.wasAssociatedWith, software_agent)) - if software is not None: - graph.add((software_agent, RDFS.label, Literal(software))) - - -def annotate_dft(graph, - structure, - id = None, - pressure = None, - software_id = None, - software = None, - ): - """ - Annotate a given structure with MD simulation details - """ - main_id = str(uuid.uuid4()) - if id is not None: - main_id = id + raise KeyError('method dict keys should be either md or dft') + - sample = structure.sample - graph.add((sample, RDF.type, PROV.Entity)) - - activity = URIRef(f'activity:{main_id}') - graph.add((activity, RDF.type, PROV.Activity)) - graph.add((activity, RDF.type, ASO.StructureOptimization)) - - method = URIRef(f'method:{main_id}') - graph.add((method, RDF.type, ASO.DensityFunctionalTheory)) - graph.add((activity, ASO.hasMethod, method)) - graph.add((activity, ASO.hasRelaxationDOF, ASO.AtomicPosition)) - - if pressure is None: - pass - elif np.isscalar(pressure): - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) - else: - #check if pressure is hydrostatic or not - axial_all_alike = None not in pressure[:3] and np.allclose( - pressure[:3], pressure[0] - ) - shear_all_none = all(p is None for p in pressure[3:]) - shear_all_zero = None not in pressure[3:] and np.allclose(pressure[3:], 0) - hydrostatic = axial_all_alike and (shear_all_none or shear_all_zero) - if hydrostatic: - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) + #add activity + main_id = mdict['id'] + activity = URIRef(f'activity:{main_id}') + self.kg.add((activity, RDF.type, PROV.Activity)) + + #method, this is specific to dft/md + if method_type == 'md': + method = URIRef(f'method:{main_id}') + if mdict['method'] == 'MolecularStatics': + self.kg.add((method, RDF.type, ASO.MolecularStatics)) + elif mdict['method'] == 'MolecularDynamics': + self.kg.add((method, RDF.type, ASO.MolecularDynamics)) + elif method_type == 'dft': + method = URIRef(f'method:{main_id}') + if mdict['method'] == 'DensityFunctionalTheory': + self.kg.add((method, RDF.type, ASO.DensityFunctionalTheory)) + self.kg.add((activity, ASO.hasMethod, method)) + + if len(mdict['dof']) == 0: + self.kg.add((activity, RDF.type, ASO.RigidEnergyCalculation)) else: - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellVolume)) - graph.add((activity, ASO.hasRelaxationDOF, ASO.CellShape)) - - - graph.add((sample, PROV.wasGeneratedBy, activity)) - - if software_id is not None: - software_agent = URIRef(software_id) - graph.add((software_agent, RDF.type, PROV.SoftwareAgent)) - graph.add((activity, PROV.wasAssociatedWith, software_agent)) - if software is not None: - graph.add((software_agent, RDFS.label, Literal(software))) - - -def _get_inherited_properties(kg, from_sample, to_sample): - #Here we need to add inherited info: CalculatedProperties will be lost - #Defects will be inherited - #add vac stuff - material = list([k[2] for k in kg.graph.triples((from_sample, CMSO.hasMaterial, None))])[0] - defects = list([x[2] for x in kg.graph.triples((material, CMSO.hasDefect, None))]) - #now for each defect we copy add this to the final sample - final_material = list([k[2] for k in kg.graph.triples((to_sample, CMSO.hasMaterial, None))])[0] - - for defect in defects: - new_defect = URIRef(defect.toPython()) - kg.graph.add((final_material, CMSO.hasDefect, new_defect)) - #now fetch all defect based info - for triple in kg.graph.triples((defect, None, None)): - kg.graph.add((new_defect, triple[1], triple[2])) - - #now add the special props for vacancy - initial_simcell = kg.graph.value(from_sample, CMSO.hasSimulationCell) - final_simcell = kg.graph.value(to_sample, CMSO.hasSimulationCell) - for triple in kg.graph.triples((initial_simcell, PODO.hasVacancyConcentration, None)): - kg.graph.add((final_simcell, triple[1], triple[2])) - for triple in kg.graph.triples((initial_simcell, PODO.hasNumberOfVacancies, None)): - kg.graph.add((final_simcell, triple[1], triple[2])) - -def _get_lattice_properties(kg, from_sample, to_sample): - material = list([k[2] for k in kg.graph.triples((from_sample, CMSO.hasMaterial, None))])[0] - crystal_structure = kg.graph.value(material, CMSO.hasStructure) - altname = kg.graph.value(crystal_structure, CMSO.hasAltName) - - #add this to new structure - final_material = list([k[2] for k in kg.graph.triples((to_sample, CMSO.hasMaterial, None))])[0] - final_crystal_structure = kg.graph.value(final_material, CMSO.hasStructure) - kg.add((final_crystal_structure, CMSO.hasAltName, altname)) - - #space group - space_group = kg.graph.value(crystal_structure, CMSO.hasSpaceGroup) - final_space_group = kg.graph.value(final_crystal_structure, CMSO.hasSpaceGroup) - for triple in kg.graph.triples((space_group, None, None)): - kg.graph.add((final_space_group, triple[1], triple[2])) - - #unit cell - unit_cell = kg.graph.value(crystal_structure, CMSO.hasUnitCell) - bv = kg.graph.value(unit_cell, CMSO.hasBravaisLattice) - - final_unit_cell = kg.graph.value(final_crystal_structure, CMSO.hasUnitCell) - kg.graph.add((final_unit_cell, CMSO.hasBravaisLattice, bv)) - - #lattice parameter - lattice_parameter = kg.graph.value(unit_cell, CMSO.hasLatticeParameter) - final_lattice_parameter = kg.graph.value(final_unit_cell, CMSO.hasLatticeParameter) - for triple in kg.graph.triples((lattice_parameter, None, None)): - kg.graph.add((final_lattice_parameter, triple[1], triple[2])) - - #lattice angle - lattice_angle = kg.graph.value(unit_cell, CMSO.hasAngle) - final_lattice_angle = kg.graph.value(final_unit_cell, CMSO.hasAngle) - for triple in kg.graph.triples((lattice_angle, None, None)): - kg.graph.add((final_lattice_angle, triple[1], triple[2])) - - -def add_derived_structure(kg, initial_sample, final_sample): - _get_inherited_properties(kg, initial_sample, final_sample) - _get_lattice_properties(kg, initial_sample, final_sample) - - kg.add((initial_sample, RDF.type, PROV.Entity)) - kg.add((final_sample, RDF.type, PROV.Entity)) - kg.add((final_sample, PROV.wasDerivedFrom, initial_sample)) - -def add_method(kg, mdict): - main_id = mdict['id'] - activity = URIRef(f'activity:{main_id}') - kg.add((activity, RDF.type, PROV.Activity)) - - if len(mdict['dof']) == 0: - kg.add((activity, RDF.type, ASO.RigidEnergyCalculation)) - else: - kg.add((activity, RDF.type, ASO.StructureOptimization)) - - method = URIRef(f'method:{main_id}') - if mdict['method'] == 'MolecularStatics': - kg.add((method, RDF.type, ASO.MolecularStatics)) - elif mdict['method'] == 'MolecularDynamics': - kg.add((method, RDF.type, ASO.MolecularDynamics)) - kg.add((activity, ASO.hasMethod, method)) - - for dof in mdict['dof']: - kg.add((activity, ASO.hasRelaxationDOF, getattr(ASO, dof))) - - kg.add((method, ASO.hasStatisticalEnsemble, getattr(ASO, mdict['ensemble']))) - - - #add temperature if needed - if mdict['temperature'] is not None: - temperature = URIRef(f'temperature:{main_id}') - kg.add((temperature, RDF.type, ASO.InputParameter)) - kg.add((temperature, RDFS.label, Literal('temperature', datatype=XSD.string))) - kg.add((activity, ASO.hasInputParameter, temperature)) - kg.add((temperature, ASO.hasValue, Literal(mdict['temperature'], datatype=XSD.float))) - kg.add((temperature, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/K'))) - - if mdict['pressure'] is not None: - pressure = URIRef(f'pressure:{main_id}') - kg.add((pressure, RDF.type, ASO.InputParameter)) - kg.add((pressure, RDFS.label, Literal('pressure', datatype=XSD.string))) - kg.add((activity, ASO.hasInputParameter, pressure)) - kg.add((pressure, ASO.hasValue, Literal(mdict['pressure'], datatype=XSD.float))) - kg.add((pressure, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/GigaPA'))) + self.kg.add((activity, RDF.type, ASO.StructureOptimization)) + + for dof in mdict['dof']: + self.kg.add((activity, ASO.hasRelaxationDOF, getattr(ASO, dof))) + + if method_type == 'md': + self.kg.add((method, ASO.hasStatisticalEnsemble, getattr(ASO, mdict['ensemble']))) + + #add temperature if needed + if mdict['temperature'] is not None: + temperature = URIRef(f'temperature:{main_id}') + self.kg.add((temperature, RDF.type, ASO.InputParameter)) + self.kg.add((temperature, RDFS.label, Literal('temperature', datatype=XSD.string))) + self.kg.add((activity, ASO.hasInputParameter, temperature)) + self.kg.add((temperature, ASO.hasValue, Literal(mdict['temperature'], datatype=XSD.float))) + self.kg.add((temperature, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/K'))) + + if mdict['pressure'] is not None: + pressure = URIRef(f'pressure:{main_id}') + self.kg.add((pressure, RDF.type, ASO.InputParameter)) + self.kg.add((pressure, RDFS.label, Literal('pressure', datatype=XSD.string))) + self.kg.add((activity, ASO.hasInputParameter, pressure)) + self.kg.add((pressure, ASO.hasValue, Literal(mdict['pressure'], datatype=XSD.float))) + self.kg.add((pressure, ASO.hasUnit, URIRef('http://qudt.org/vocab/unit/GigaPA'))) + + #potentials need to be mapped + potential = URIRef(f'potential:{main_id}') + if 'meam' in mdict['potential']['type']: + self.kg.add((potential, RDF.type, ASO.MEAM)) + elif 'eam' in mdict['potential']['type']: + self.kg.add((potential, RDF.type, ASO.EAM)) + elif 'lj' in mdict['potential']['type']: + self.kg.add((potential, RDF.type, ASO.LennardJones)) + elif 'ace' in mdict['potential']['type']: + self.kg.add((potential, RDF.type, ASO.MLPotential)) + else: + self.kg.add((potential, RDF.type, ASO.InteratomicPotential)) + + if 'uri' in mdict['potential'].keys(): + self.kg.add((potential, ASO.hasReference, Literal(mdict['potential']['uri']))) + if 'label' in mdict['potential'].keys(): + self.kg.add((potential, RDFS.label, Literal(mdict['potential']['label']))) + + self.kg.add((method, ASO.hasInteratomicPotential, potential)) + + self.kg.add((self.sample, PROV.wasGeneratedBy, activity)) + + #finally add software + wfagent = None + if 'workflow_manager' in mdict.keys(): + wfagent = URIRef(mdict["workflow_manager"]['uri']) + self.kg.add((wfagent, RDF.type, PROV.SoftwareAgent)) + self.kg.add((wfagent, RDFS.label, Literal(mdict["workflow_manager"]['label']))) + + for software in mdict['software'].keys(): + agent = URIRef(software['uri']) + self.kg.add((agent, RDF.type, PROV.SoftwareAgent)) + self.kg.add((agent, RDFS.label, software['label'])) + if wfagent is not None: + self.kg.add((wfagent, PROV.actedOnBehalfOf, agent)) From ea5c9df96d64ea00b4059d2db390d91f65c4e059 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Wed, 10 Apr 2024 15:28:55 +0200 Subject: [PATCH 31/38] bug fixes in workflow --- pyscal_rdf/workflow/pyiron.py | 79 +++++++++++++++++++-------------- pyscal_rdf/workflow/workflow.py | 14 +++--- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 4034a3f..12e1419 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -7,7 +7,7 @@ from pyscal_rdf.structure import _make_crystal from pyscal_rdf.structure import System from pyscal3.core import structure_dict, element_dict - +import ast def _check_if_job_is_valid(job): valid_jobs = ['Lammps', ] @@ -23,22 +23,12 @@ def _add_structures(kg, job): initial_sample_id = None if 'sample_id' in initial_pyiron_structure.info.keys(): - if not initial_pyiron_structure.info['sample_id'] in kg.samples: - kg.add_structure(initial_pyscal_structure) - else: - initial_sample_id = initial_pyiron_structure.info['sample_id'] - else: - kg.add_structure(initial_pyscal_structure) - - if initial_sample_id is None: - initial_sample_id = initial_pyscal_structure.sample - + initial_sample_id = initial_pyiron_structure.info['sample_id'] #add final structure - final_pyscal_structure = System.read.ase(final_pyiron_structure, graph=kg) - final_sample_id = final_pyscal_structure.sample - + final_pyscal_structure = System.read.ase(final_pyiron_structure) + #now we do rthe transfer - return final_sample_id + return initial_pyscal_structure, initial_sample_id, final_pyscal_structure, None def _identify_method(job): @@ -90,30 +80,53 @@ def _identify_method(job): press = float(raw[7]) ensemble = 'NPT' - mdict = {} - mdict['method'] = md_method - mdict['temperature'] = temp - mdict['pressure'] = press - mdict['dof'] = dof - mdict['ensemble'] = ensemble - mdict['id'] = job.id - - return mdict + mdict['md'] = {} + mdict['md']['method'] = md_method + mdict['md']['temperature'] = temp + mdict['md']['pressure'] = press + mdict['md']['dof'] = dof + mdict['md']['ensemble'] = ensemble + mdict['md']['id'] = job.id + + #now process potential + inpdict = job.input.to_dict() + ps = inpdict['potential_inp/data_dict']['Value'][0] + name = inpdict['potential_inp/potential/Name'] + potstr = job.input.to_dict()['potential_inp/potential/Citations'] + potdict = ast.literal_eval(potstr[1:-1]) + url = None + if 'url' in potdict[list(potdict.keys())[0]].keys(): + url = potdict[list(potdict.keys())[0]]['url'] + + mdict['md']['potential'] = {} + mdict['md']['potential']['type'] = ps + mdict['md']['potential']['label'] = name + if url is not None: + mdict['md']['potential']['uri'] = url + else: + mdict['md']['potential']['uri'] = name + + mdict['md']['workflow_manager'] = {} + mdict['md']['workflow_manager']['uri'] = "http://demo.fiz-karlsruhe.de/matwerk/E457491" + mdict['md']['workflow_manager']['label'] = "pyiron" + #and finally code details -def _add_method(kg, job): - mdict = _identify_method(job) - activity_id = wf.add_method(kg, mdict) - return activity_id + + software = {'uri':"http://demo.fiz-karlsruhe.de/matwerk/E447986", + 'label':'LAMMPS'} + mdict['md']['software'] = [software] -def add_mappings(kg, job): - final_sample_id = _add_structures(kg, job) - final_sample_id = wf.add_derived_structure(kg, initial_sample_id, final_sample_id) + return mdict - activity_id = _add_method(kg, job) - +def add_mappings(kg, job): + initial_structure, initial_sample, final_structure, final_sample = _add_structures(kg, job) + mdict = _identify_method(job) + workflow = wf.Workflow(kg, structure=final_structure, + sample=final_sample, parent_structure=initial_structure, + parent_sample=initial_sample, method_dict=mdict) def update_project(pr, kg): """ diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index ceea86f..cc71ad8 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -40,6 +40,7 @@ def __init__(self, kg, parent_structure.to_graph() parent_sample = parent_structure.sample + self.sample = sample self.mdict = method_dict self.parent_sample = parent_sample self.add_structural_relation() @@ -56,7 +57,7 @@ def _add_inherited_properties(self, ): #now for each defect we copy add this to the final sample material = list([k[2] for k in self.kg.graph.triples((self.sample, CMSO.hasMaterial, None))])[0] - for defect in defects: + for defect in parent_defects: new_defect = URIRef(defect.toPython()) self.kg.graph.add((material, CMSO.hasDefect, new_defect)) #now fetch all defect based info @@ -64,8 +65,8 @@ def _add_inherited_properties(self, ): self.kg.graph.add((new_defect, triple[1], triple[2])) #now add the special props for vacancy - parent_simcell = kg.graph.value(self.sample, CMSO.hasSimulationCell) - simcell = kg.graph.value(self.parent_sample, CMSO.hasSimulationCell) + parent_simcell = self.kg.graph.value(self.sample, CMSO.hasSimulationCell) + simcell = self.kg.graph.value(self.parent_sample, CMSO.hasSimulationCell) for triple in self.kg.graph.triples((parent_simcell, PODO.hasVacancyConcentration, None)): self.kg.graph.add((simcell, triple[1], triple[2])) @@ -234,11 +235,14 @@ def add_method(self, ): wfagent = URIRef(mdict["workflow_manager"]['uri']) self.kg.add((wfagent, RDF.type, PROV.SoftwareAgent)) self.kg.add((wfagent, RDFS.label, Literal(mdict["workflow_manager"]['label']))) + self.kg.add((method, PROV.wasAssociatedWith, wfagent)) - for software in mdict['software'].keys(): + for software in mdict['software']: agent = URIRef(software['uri']) self.kg.add((agent, RDF.type, PROV.SoftwareAgent)) - self.kg.add((agent, RDFS.label, software['label'])) + self.kg.add((agent, RDFS.label, Literal(software['label']))) if wfagent is not None: self.kg.add((wfagent, PROV.actedOnBehalfOf, agent)) + else: + self.kg.add((method, PROV.wasAssociatedWith, agent)) From 19dc0b3497caf68324b1ababb7d81a852a7a4fb3 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 10:36:47 +0200 Subject: [PATCH 32/38] restrutcure workflow --- pyscal_rdf/workflow/pyiron.py | 10 ++-------- pyscal_rdf/workflow/workflow.py | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 12e1419..49a5786 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -121,14 +121,8 @@ def _identify_method(job): return mdict -def add_mappings(kg, job): - initial_structure, initial_sample, final_structure, final_sample = _add_structures(kg, job) - mdict = _identify_method(job) - workflow = wf.Workflow(kg, structure=final_structure, - sample=final_sample, parent_structure=initial_structure, - parent_sample=initial_sample, method_dict=mdict) - -def update_project(pr, kg): + +def inform_graph(pr, kg): """ Update project to add extra creator functions """ diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index cc71ad8..06ddc0f 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -6,6 +6,8 @@ from pyscal_rdf.structure import System from rdflib import Graph, Literal, Namespace, XSD, RDF, RDFS, BNode, URIRef, FOAF, SKOS, DCTERMS +import pyscal_rdf.workflow.pyiron as pi + import warnings import numpy as np import os @@ -20,11 +22,19 @@ class Workflow: def __init__(self, kg, - structure=None, sample=None, - parent_structure=None, parent_sample=None, - method_dict=None): + workflow_object, + environment='pyiron'): self.kg = kg + if environment == 'pyiron': + self.wenv = pi + else: + raise ValueError('unknow workflow environment') + + pi._check_if_job_is_valid(workflow_object) + parent_structure, parent_sample, structure, sample = pi._add_structures(kg, workflow_object) + method_dict = pi._identify_method(workflow_object) + if (structure is None) and (sample is None): raise ValueError('Either structure or sample should be specified') @@ -43,8 +53,7 @@ def __init__(self, kg, self.sample = sample self.mdict = method_dict self.parent_sample = parent_sample - self.add_structural_relation() - self.add_method() + def _add_inherited_properties(self, ): #Here we need to add inherited info: CalculatedProperties will be lost @@ -246,3 +255,7 @@ def add_method(self, ): self.kg.add((wfagent, PROV.actedOnBehalfOf, agent)) else: self.kg.add((method, PROV.wasAssociatedWith, agent)) + + def to_graph(self): + self.add_structural_relation() + self.add_method() \ No newline at end of file From 4d6d4b7e2dbe4affab778b7cce157e48a4a074be Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 11:13:32 +0200 Subject: [PATCH 33/38] add importable workflow objexts --- pyscal_rdf/stores.py | 2 +- pyscal_rdf/workflow/__init__.py | 2 +- pyscal_rdf/workflow/workflow.py | 13 ++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index 46bb386..aab6e20 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -4,7 +4,7 @@ import os #special methods; for supporting workflow envs -from pyscal_rdf.workflow import update_project +from pyscal_rdf.workflow import inform_graph def create_store(kg, store, identifier, diff --git a/pyscal_rdf/workflow/__init__.py b/pyscal_rdf/workflow/__init__.py index 7369f13..67063bf 100644 --- a/pyscal_rdf/workflow/__init__.py +++ b/pyscal_rdf/workflow/__init__.py @@ -1,2 +1,2 @@ -from pyscal_rdf.workflow.pyiron import update_project +from pyscal_rdf.workflow.pyiron import inform_graph diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 06ddc0f..786fdee 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -22,18 +22,17 @@ class Workflow: def __init__(self, kg, - workflow_object, environment='pyiron'): self.kg = kg - if environment == 'pyiron': self.wenv = pi else: raise ValueError('unknow workflow environment') - pi._check_if_job_is_valid(workflow_object) - parent_structure, parent_sample, structure, sample = pi._add_structures(kg, workflow_object) - method_dict = pi._identify_method(workflow_object) + def _prepare_job(self, workflow_object): + self.wenv._check_if_job_is_valid(workflow_object) + parent_structure, parent_sample, structure, sample = self.wenv._add_structures(kg, workflow_object) + method_dict = self.wenv._identify_method(workflow_object) if (structure is None) and (sample is None): raise ValueError('Either structure or sample should be specified') @@ -54,7 +53,6 @@ def __init__(self, kg, self.mdict = method_dict self.parent_sample = parent_sample - def _add_inherited_properties(self, ): #Here we need to add inherited info: CalculatedProperties will be lost #Defects will be inherited @@ -256,6 +254,7 @@ def add_method(self, ): else: self.kg.add((method, PROV.wasAssociatedWith, agent)) - def to_graph(self): + def to_graph(self, workflow_object): + self._prepare_job(workflow_object) self.add_structural_relation() self.add_method() \ No newline at end of file From 35ff5462a66173f54e363c20585d40d187138d26 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 11:23:33 +0200 Subject: [PATCH 34/38] fix kg call --- pyscal_rdf/__init__.py | 3 ++- pyscal_rdf/stores.py | 2 +- pyscal_rdf/workflow/workflow.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyscal_rdf/__init__.py b/pyscal_rdf/__init__.py index 33f0026..b8764a2 100644 --- a/pyscal_rdf/__init__.py +++ b/pyscal_rdf/__init__.py @@ -1,2 +1,3 @@ from pyscal_rdf.graph import KnowledgeGraph -from pyscal_rdf.structure import System \ No newline at end of file +from pyscal_rdf.structure import System +from pyscal_rdf.workflow.workflow import Workflow \ No newline at end of file diff --git a/pyscal_rdf/stores.py b/pyscal_rdf/stores.py index aab6e20..2099ebc 100644 --- a/pyscal_rdf/stores.py +++ b/pyscal_rdf/stores.py @@ -44,7 +44,7 @@ def store_pyiron(kg, store, identifier, store_file=None, structure_store=None): store_file = os.path.join(store.path, f'{store.name}.db') store_alchemy(kg, store, identifier, store_file, structure_store=structure_store) #finally update project object - update_project(store, kg) + inform_graph(store, kg) def _check_if_sqlalchemy_is_available(): try: diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 786fdee..723ea8a 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -39,13 +39,13 @@ def _prepare_job(self, workflow_object): if sample is None: #its not added to graph yet - structure.graph = kg + structure.graph = self.kg structure.to_graph() sample = structure.sample if parent_sample is None: #its not added to graph yet - parent_structure.graph = kg + parent_structure.graph = self.kg parent_structure.to_graph() parent_sample = parent_structure.sample From f6628fe0b95a7a2bda89c2e154c2e25505f62d1f Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 11:26:24 +0200 Subject: [PATCH 35/38] fix kg call --- pyscal_rdf/workflow/workflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 723ea8a..e10f891 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -31,7 +31,7 @@ def __init__(self, kg, def _prepare_job(self, workflow_object): self.wenv._check_if_job_is_valid(workflow_object) - parent_structure, parent_sample, structure, sample = self.wenv._add_structures(kg, workflow_object) + parent_structure, parent_sample, structure, sample = self.wenv._add_structures(self.kg, workflow_object) method_dict = self.wenv._identify_method(workflow_object) if (structure is None) and (sample is None): From 1a41ffa127d08e43e5e72d27ef3c1d68a2999d80 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 12:35:17 +0200 Subject: [PATCH 36/38] add possibility to add calculated properties --- pyscal_rdf/workflow/pyiron.py | 22 ++++++++++++++++++++++ pyscal_rdf/workflow/workflow.py | 15 ++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 49a5786..13ad67a 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -2,6 +2,7 @@ Wrappers for pyiron jobs """ import os +import numpy as np from functools import partial, update_wrapper import pyscal_rdf.workflow.workflow as wf from pyscal_rdf.structure import _make_crystal @@ -118,9 +119,30 @@ def _identify_method(job): 'label':'LAMMPS'} mdict['md']['software'] = [software] + #finally add calculated quantities + quantdict = extract_calculated_quantities(job) + mdict['md']['outputs'] = quantdict return mdict +def extract_calculated_quantities(job): + aen = np.mean(job.output.energy_tot) + avol = np.mean(job.output.volume) + outdict = {} + outdict['TotalEnergy'] = {} + outdict['TotalEnergy']['value'] = np.round(aen, decimals=4) + outdict['TotalEnergy']['unit'] = 'EV' + outdict['TotalEnergy']['associate_to_sample'] = True + + + outdict['TotalVolume'] = {} + outdict['TotalVolume']['value'] = np.round(avol, decimals=4) + outdict['TotalVolume']['unit'] = 'ANGSTROM3' + outdict['TotalVolume']['associate_to_sample'] = True + + return outdict + + def inform_graph(pr, kg): """ diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index e10f891..8f6cb8f 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -254,7 +254,20 @@ def add_method(self, ): else: self.kg.add((method, PROV.wasAssociatedWith, agent)) + for key, val in mdict['outputs'].items(): + prop = URIRef(f'{main_id}_{key}') + self.kg.add((prop, RDF.type, CMSO.CalculatedProperty)) + self.kg.add((prop, RDFS.label, Literal(key))) + self.kg.add((prop, CMSO.hasValue, Literal(val["value"]))) + if "unit" in val.keys(): + unit = val['unit'] + self.kg.add((prop, CMSO.hasUnit, URIRef(f'http://qudt.org/vocab/unit/{unit}'))) + + self.kg.add((prop, CMSO.wasCalculatedBy, activity)) + if val['associate_to_sample']: + self.kg.add((sample, CMSO.hasCalculatedProperty, prop)) + def to_graph(self, workflow_object): self._prepare_job(workflow_object) self.add_structural_relation() - self.add_method() \ No newline at end of file + self.add_method() From bb14da322abed1d4af897df44575394a17b12197 Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 13:35:23 +0200 Subject: [PATCH 37/38] fix added quantities --- pyscal_rdf/visualize.py | 3 ++- pyscal_rdf/workflow/workflow.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyscal_rdf/visualize.py b/pyscal_rdf/visualize.py index 102ed01..281bd1c 100644 --- a/pyscal_rdf/visualize.py +++ b/pyscal_rdf/visualize.py @@ -99,9 +99,10 @@ def visualize_graph(g, #we collapse sample information #if cmso.connector is found, only use it is it is cmso.hasCalculated #all sub sample props, indicated by sample_x_jsjsj will be ignored. + green_list = ["hasCalculatedProperty", "wasCalculatedBy", "hasValue"] ssplit = string3.split('.') if (len(ssplit) == 2): - if (ssplit[0] == 'cmso') and (ssplit[1] != "hasCalculatedProperty"): + if (ssplit[0] == 'cmso') and (ssplit[1] not in green_list): plot = False if string3 == 'subClassOf': plot = False diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 8f6cb8f..936af82 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -258,14 +258,13 @@ def add_method(self, ): prop = URIRef(f'{main_id}_{key}') self.kg.add((prop, RDF.type, CMSO.CalculatedProperty)) self.kg.add((prop, RDFS.label, Literal(key))) - self.kg.add((prop, CMSO.hasValue, Literal(val["value"]))) + self.kg.add((prop, ASO.hasValue, Literal(val["value"]))) if "unit" in val.keys(): unit = val['unit'] - self.kg.add((prop, CMSO.hasUnit, URIRef(f'http://qudt.org/vocab/unit/{unit}'))) - + self.kg.add((prop, ASO.hasUnit, URIRef(f'http://qudt.org/vocab/unit/{unit}'))) self.kg.add((prop, CMSO.wasCalculatedBy, activity)) if val['associate_to_sample']: - self.kg.add((sample, CMSO.hasCalculatedProperty, prop)) + self.kg.add((self.sample, CMSO.hasCalculatedProperty, prop)) def to_graph(self, workflow_object): self._prepare_job(workflow_object) From 470a7e08b6b7268122081327096573269f06623c Mon Sep 17 00:00:00 2001 From: Sarath Menon Date: Thu, 11 Apr 2024 15:39:07 +0200 Subject: [PATCH 38/38] minor update to workflows --- environment.yml | 3 +++ pyscal_rdf/workflow/pyiron.py | 2 +- pyscal_rdf/workflow/workflow.py | 30 +++++++++++++++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/environment.yml b/environment.yml index 32ab4ae..e6ca18c 100644 --- a/environment.yml +++ b/environment.yml @@ -16,3 +16,6 @@ dependencies: - owlready2 - plotly - ipywidgets + - sqlalchemy + - pip: + - "git+https://github.com/RDFLib/rdflib-sqlalchemy.git@develop" diff --git a/pyscal_rdf/workflow/pyiron.py b/pyscal_rdf/workflow/pyiron.py index 13ad67a..d1cf6f8 100644 --- a/pyscal_rdf/workflow/pyiron.py +++ b/pyscal_rdf/workflow/pyiron.py @@ -17,7 +17,7 @@ def _check_if_job_is_valid(job): raise TypeError('These type of pyiron Job is not currently supported') -def _add_structures(kg, job): +def _add_structures(job): initial_pyiron_structure = job.structure final_pyiron_structure = job.get_structure(frame=-1) initial_pyscal_structure = System.read.ase(initial_pyiron_structure) diff --git a/pyscal_rdf/workflow/workflow.py b/pyscal_rdf/workflow/workflow.py index 936af82..1e29788 100644 --- a/pyscal_rdf/workflow/workflow.py +++ b/pyscal_rdf/workflow/workflow.py @@ -1,13 +1,22 @@ """ Workflows aspects for non-automated annotation of structures. +This consists of a workflow class which implements the necessary methods to serialise triples as needed. +Custom workflow solutions can be implemented. An example available here is pyiron. +The custom workflow env should implement the following functions: + +_check_if_job_is_valid +_add_structure +_identify_method +extract_calculated_properties +inform_graph + +See pyscal_rdf.workflow.pyiron for more details """ from pyscal_rdf.structure import System from rdflib import Graph, Literal, Namespace, XSD, RDF, RDFS, BNode, URIRef, FOAF, SKOS, DCTERMS -import pyscal_rdf.workflow.pyiron as pi - import warnings import numpy as np import os @@ -15,14 +24,29 @@ import ast import uuid +#Move imports to another file PROV = Namespace("http://www.w3.org/ns/prov#") CMSO = Namespace("http://purls.helmholtz-metadaten.de/cmso/") PODO = Namespace("http://purls.helmholtz-metadaten.de/podo/") ASO = Namespace("http://purls.helmholtz-metadaten.de/aso/") +#custom imports as needed +import pyscal_rdf.workflow.pyiron as pi + + class Workflow: def __init__(self, kg, environment='pyiron'): + """ + Initialize the workflow environment + + Parameters + ---------- + kg: pyscal-rdf KnowledgeGraph + environment: string + the workflow environment. This is used to import the necessary functions. + + """ self.kg = kg if environment == 'pyiron': self.wenv = pi @@ -31,7 +55,7 @@ def __init__(self, kg, def _prepare_job(self, workflow_object): self.wenv._check_if_job_is_valid(workflow_object) - parent_structure, parent_sample, structure, sample = self.wenv._add_structures(self.kg, workflow_object) + parent_structure, parent_sample, structure, sample = self.wenv._add_structures(workflow_object) method_dict = self.wenv._identify_method(workflow_object) if (structure is None) and (sample is None):