diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b8167a8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.vscode/ +docs/build +*.pyc +build/ +dist/ +*.egg-info/ +.eggs/ \ No newline at end of file diff --git a/.github/workflows/CODEOWNERS b/.github/workflows/CODEOWNERS new file mode 100644 index 0000000..3851dec --- /dev/null +++ b/.github/workflows/CODEOWNERS @@ -0,0 +1,4 @@ +# follows the same pattern as .gitignore files +# file/mask username1/email1 ... usernameN/emailN + +* @yoavnash @pablo-de-andres @kysrpex diff --git a/README.md b/README.md index 29e99fe..110a72a 100644 --- a/README.md +++ b/README.md @@ -3,27 +3,50 @@ **dev** - [![Documentation dev](https://readthedocs.org/projects/simphony/badge/?version=dev)](https://simphony.readthedocs.io/en/latest/?badge=dev) # SimPhoNy docs -To access the documentation, please visit: https://simphony.readthedocs.io/en/latest/ +To access the documentation, please visit: [https://simphony.readthedocs.io](https://simphony.readthedocs.io). -If you find any error or problem with the documentation, please [create an issue](https://github.com/simphony/docs/issues) +If you find any error or problem with the documentation, please [create an issue](https://github.com/simphony/docs/issues). -## Local installation -First, some requirements have to be manually installed: -- [osp-core](https://github.com/simphony/osp-core) -- pandoc -- LaTeX requirements - ```shell - sudo apt install pandoc - sudo apt-get install texlive-latex-recommended \ - texlive-latex-extra \ - texlive-fonts-recommended \ - latexmk - ``` +## Local Rendering +### HTML +A server will start, generate the docs and listen for changes in the source files. +This can be done by using docker or installing the development environment directly on the you machine. Next are installation guides for Docker and Linux OS. +#### Docker +First, build the Docker image by running the following command: +```shell +$ docker build -f local_build.Dockerfile -t simphony-docs . +``` + +Then, start the program by running: +```shell +$ docker run --rm -v $PWD:/app -p 8000:8000 simphony-docs +``` + +#### Linux +At an OS level (these commands work on Linux Debian): +```shell +$ sudo apt install pandoc graphviz default-jre +$ sudo apt-get install texlive-latex-recommended \ + texlive-latex-extra \ + texlive-fonts-recommended \ + latexmk +``` +The python dependencies: +```shell +$ pip install -r requirements.txt +``` -If you want to render the documentation locally, you can run: +Now you can start the server and render the docs: ``` -python3 setup.py install +$ sphinx-autobuild docs/source docs/build/html ``` +The documentation will be available on [`http://127.0.0.1:8000`](http://127.0.0.1:8000). -This will render the html and generate a LaTeX pdf (in `docs/build/latex/SimPhoNy_docs.pdf`). \ No newline at end of file + +### PDF (LaTeX) +To generate a PDF of the documentation, simply run (from the root project folder): +```sh +make -C docs latexpdf +``` +The generated PDF can be found under docs/build/latex/SymPhoNy_docs.pdf \ No newline at end of file diff --git a/docs/source/_static/img/branch_workflow.png b/docs/source/_static/img/branch_workflow.png new file mode 100644 index 0000000..0987c3b Binary files /dev/null and b/docs/source/_static/img/branch_workflow.png differ diff --git a/docs/source/_static/img/mergedlinkeddata.svg b/docs/source/_static/img/mergedlinkeddata.svg new file mode 100644 index 0000000..bddd379 --- /dev/null +++ b/docs/source/_static/img/mergedlinkeddata.svg @@ -0,0 +1,3 @@ + + +
Old city
Old city
Port district
Port distri...
Wang
Wang
Anne
Anne
Klaus
Klaus
Fabrizio
Fabrizio
livesIn
livesIn
livesIn
livesIn
livesIn
livesIn
livesIn
livesIn
hasWorkingHours
hasWorkingHours
08:30-14:00
08:30-14:00
09:00-12:00
09:00-12:00
08:30-14:00
08:30-14:00
08:00-13:00
08:00-13:00
hasWorkingHours
hasWorkingHours
hasWorkingHours
hasWorkingHours
hasWorkingHours
hasWorkingHours
St. NIcholas St.
St. NIcholas St.
Old market St.
Old market St.
adjacent
adjacent
Riverside St.
River...
Campus
Campus
adjacent
adjacent
adjacent
adjacent
adjacent
adjacent
adjacent
adjacent
hasLength
hasLength
3 km
3 km
hasLanes
hasLanes
3
3
hasSpeedLimit
hasSpeedLimit
50 kmh⁻¹
50 kmh⁻¹
hasLength
hasLength
5 km
5 km
hasLanes
hasLanes
2
2
hasSpeedLimit
hasSpeedLimit
80 kmh⁻¹
80 kmh⁻¹
2 km
2 km
1
1
30 kmh⁻¹
30 kmh⁻¹
hasLength
hasLength
hasSpeedLimit
hasSpeedLimit
hasLanes
hasLanes
adjacent
adjacent
Data from traffic authority
Data from traffic authority
Data from a city guide
Data from a city guide
Data from the university registry
Data from the university registry
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/source/_static/img/overview_slider.png b/docs/source/_static/img/overview_slider.png new file mode 100644 index 0000000..b193c10 Binary files /dev/null and b/docs/source/_static/img/overview_slider.png differ diff --git a/docs/source/_static/img/trafficsim.svg b/docs/source/_static/img/trafficsim.svg new file mode 100644 index 0000000..deef2bf --- /dev/null +++ b/docs/source/_static/img/trafficsim.svg @@ -0,0 +1,1253 @@ + + + + + + + + 2021-04-09T12:45:54.540360 + image/svg+xml + + + Matplotlib v3.4.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/img/wrappers.svg b/docs/source/_static/img/wrappers.svg new file mode 100644 index 0000000..1aeaebd --- /dev/null +++ b/docs/source/_static/img/wrappers.svg @@ -0,0 +1,3 @@ + + +
Simulation engine wrapper
Simulation...
SQLite Database
SQLite Database
Traffic simulation engine
Traffic simulation engine
SQLite Wrapper
SQLite Wrap...
Transfer ontology
individuals (data)
Transfer ontology...
SQLite Database
SQLite Database
SQLite Wrapper
SQLite Wrap...
Transfer results back
Tran...
Simulation engine wrapper
Simulation...
Traffic simulation engine (finished)
Traffic simulation engine (finished)
Run simulation
Run simula...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/source/_static/img/wrappers_coupled.svg b/docs/source/_static/img/wrappers_coupled.svg new file mode 100644 index 0000000..ea77b03 --- /dev/null +++ b/docs/source/_static/img/wrappers_coupled.svg @@ -0,0 +1,3 @@ + + +
Simulation engine wrapper
Simulation...
SQLite Database
SQLite Database
Molecular dynamics engine
Molecular dynamics engine
SQLite Wrapper
SQLite Wrap...
Transfer ontology individuals (data)
Transfer ontology individuals (data)
SQLite Database
SQLite Database
SQLite Wrapper
SQLite Wrap...
Transfer results back
Transfer results back
Simulation engine wrapper
Simulation...
Fluid dynamics engine
Fluid dynamics engine
Run some simulation steps,
transfer data
Run some simulation steps,...
Run some simulation steps, transfer data
Run some simulation steps, transfer data
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/source/_static/videos/coupling_and_linking.mp4 b/docs/source/_static/videos/coupling_and_linking.mp4 new file mode 100644 index 0000000..d456370 Binary files /dev/null and b/docs/source/_static/videos/coupling_and_linking.mp4 differ diff --git a/docs/source/about.md b/docs/source/about.md deleted file mode 100644 index 32b2f09..0000000 --- a/docs/source/about.md +++ /dev/null @@ -1,53 +0,0 @@ -# About - -SimPhoNy is an ontology-based framework that promotes and enables interoperability between any 3rd-party software tool. -Its [core functionality](https://github.com/simphony/osp-core) is an open-source python project. -The name ‘SimPhoNy’ stems from the SimPhoNy EU-project in which it was originally developed -(See more details [here](https://www.simphony-project.eu/)). -One of SimPhoNy’s main tasks is to convert *opaque* data, meaning data whose semantics are hidden, to *transparent* data, that is data whose semantics is understood and easily accessible. - -This project aims to clarify the purpose and usage of the SimPhoNy platform through simple, short examples. -In particular, this guide will try to expose the main concepts and components. - -All the tutorials in this documentation are Jupyter notebooks that can be downloaded -by clicking on the "Edit on Github" button on the top right of the page. - -# License -BSD 3-Clause -Copyright 2020 SimPhoNy OSP-core developers. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Terminology -These are some of the terms used in the following sections: -1. `API`: Application Programming Interface. A set of functions that allow the interaction with an application or system. -1. `OSP`: Open Simulation Platform. - A set of common standards and related tools that form the basic environment on top of which compatible and compliant simulation workflows can be developed and run. - An OSP does not contain any simulation tools itself, it is the common framework enabling to couple and link them. -1. `backend`: a third party application or service. - Simulation engines and databases are examples of backends. -1. `wrapper`: a plugin for OSP-core that adds support to a new backend. - It must allow the user to interact with the backend through the same API as OSP-core. -1. `ontology`: an explicit, formal specification of a shared conceptualization. - In the context of ontology, other relevant terms are: - 1. `class`: a concept. E.g., 'City', 'Experiment'. - 1. `attribute`: a property of a class that has a data type. E.g., 'name' of the type String which could be used as an attribute of 'City'. - 1. `individual`: an instance of a class. E.g., an instance of the class 'City' can be used to represent the city of Freiburg in which case it would have the attribute 'name' with the value 'Freiburg'. - 1. `relationship`: a type of a way in which one individual relates to another. E.g., 'Has-A' which could use to form the relationship 'Freiburg (City) Has-A Dreisam (River)'. - 1. `entity`: a general term that can refer to a class, a relationship, attribute, or an individual. E.g., 'City', 'name', 'Has-A', the Freiburg individual are all entities. - 1. `namespace`: an ontology identifier. E.g., 'city_ontology' which could be used as a namespace for the ontology that consists of the entities 'City', 'name' and 'Has-A'. - - Each entity is uniquely identified by its name and the namespace it is contained in. We call \.\ the `qualified entity name`. -1. `CUDS`: Common Universal Data Structure. A data structure that is used to uniformly represent ontology concepts in programming code. - - CUDS exposes an API that provides CRUD (Create, Read, Update and Delete) functionalities. - - CUDS is a recursive data structure in that a CUDS object may contain other CUDS objects. - - CUDS is the fundamental data type of OSP-core, a framework that establishes interoperability between software systems that are built on top of ontologies. -1. `CUDS class`: represents an ontology class (a concept) and encodes its ontological information. -1. `CUDS object`: is an instance of a CUDS class and represents an ontology individual. diff --git a/docs/source/api_ref.md b/docs/source/api_ref.md index 61e4f1f..6fb951f 100644 --- a/docs/source/api_ref.md +++ b/docs/source/api_ref.md @@ -1,21 +1,51 @@ -# API Reference -This document is for developers of OSP-core, it contains the API functions +```eval_rst +.. raw:: html + +

API Reference

+ +``` +This document is for developers and/or advanced users of OSP-core, it contains all API details. -## Cuds class +## CUDS ```eval_rst .. autoclass:: osp.core.cuds.Cuds :members: :show-inheritance: ``` -## Registry class +## Ontology interface ```eval_rst -.. autoclass:: osp.core.session.registry.Registry +.. autoclass:: osp.core.ontology.namespace.OntologyNamespace + :members: + :special-members: __getattr__, __getitem__, __contains__, __iter__, __eq__ + :show-inheritance: + +.. autoclass:: osp.core.ontology.entity.OntologyEntity + :members: + :show-inheritance: + +.. autoclass:: osp.core.ontology.oclass.OntologyClass + :members: + :show-inheritance: + +.. autoclass:: osp.core.ontology.oclass_restriction.Restriction + :members: + :show-inheritance: + +.. autoclass:: osp.core.ontology.oclass_composition.Composition + :members: + :show-inheritance: + +.. autoclass:: osp.core.ontology.relationship.OntologyRelationship + :members: + :show-inheritance: + +.. autoclass:: osp.core.ontology.attribute.OntologyAttribute :members: :show-inheritance: ``` -## Session classes +## Sessions ```eval_rst .. autoclass:: osp.core.session.session.Session :members: @@ -42,7 +72,14 @@ This document is for developers of OSP-core, it contains the API functions :show-inheritance: ``` -## utils +## Registry +```eval_rst +.. autoclass:: osp.core.session.registry.Registry + :members: + :show-inheritance: +``` + +## Utilities ```eval_rst .. autoclass:: osp.core.utils.cuds2dot.Cuds2dot :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index d6522b6..2bbab9b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,22 +12,23 @@ # -- Project information ----------------------------------------------------- project = 'SimPhoNy' # Version is given in setuptools -copyright = '2020, Materials Data Science and Informatics team at Fraunhofer IWM' +copyright = ('2021, Materials Data Science and ' + 'Informatics Team at Fraunhofer IWM') author = 'Materials Data Science and Informatics team at Fraunhofer IWM' # -- General configuration --------------------------------------------------- extensions = [ - 'recommonmark', # md + 'recommonmark', # markdown source support 'sphinx.ext.autodoc', # API ref - 'sphinx.ext.napoleon', # API ref Google and NumPy style - 'sphinx.ext.viewcode', # API link to source + 'sphinx.ext.napoleon', # API ref Google and NumPy style + 'sphinx.ext.viewcode', # API link to source 'sphinx.ext.graphviz', # Graphviz 'sphinxcontrib.plantuml', # PlantUml 'sphinx_copybutton', # Copy button for codeblocks 'nbsphinx', # Jupyter - 'IPython.sphinxext.ipython_console_highlighting', # Jupyter - Syntax highlight workaround + 'IPython.sphinxext.ipython_console_highlighting', # nb syntax highlight 'sphinx.ext.autosectionlabel', # Auto-generate section labels. - 'sphinx-jsonschema' # Generate JSON schema for serialized CUDS + 'sphinx_panels' # Create panels in a grid layout or as drop-downs ] master_doc = 'index' @@ -35,14 +36,11 @@ plantuml = 'java -jar lib/plantuml.jar' plantuml_output_format = 'svg_img' -templates_path = ['_templates'] - # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['**.ipynb_checkpoints'] - # -- Options for HTML output ------------------------------------------------- html_theme = 'sphinx_rtd_theme' @@ -55,14 +53,12 @@ latex_documents = [('index', 'SimPhoNy_docs.tex', 'SimPhoNy documentation', - 'Materials Data Science and Informatics team at Fraunhofer IWM', + ('Materials Data Science and ' + 'Informatics team at Fraunhofer IWM'), "manual", - "false", - ),] + "false",)] latex_logo = '_static/img/simphony_logo_dark.png' -latex_elements = { 'figure_align': 'H', - } - +latex_elements = {'figure_align': 'H'} def setup(app): diff --git a/docs/source/contact.md b/docs/source/contact.md index 4293900..3003224 100644 --- a/docs/source/contact.md +++ b/docs/source/contact.md @@ -3,6 +3,6 @@ If you see something wrong, missing, or in need of clarification, you can direct create an issue in [here](https://github.com/simphony/docs/issues). Any other questions, issues or comments can be directed to [Pablo de Andres](mailto:pablo.de.andres@iwm.fraunhofer.de), -[Matthias Urban](mailto:matthias.urban@iwm.fraunhofer.de) and +[Jose Manuel Dominguez](mailto:jose.manuel.dominguez@iwm.fraunhofer.de) and [Yoav Nahshon](mailto:yoav.nahshon@iwm.fraunhofer.de) -from the Materials Data Science and Informatics team, Fraunhofer IWM. \ No newline at end of file +from the Materials Data Science and Informatics Team, Fraunhofer IWM. diff --git a/docs/source/contribute.md b/docs/source/contribute.md index 325c0cb..2774f1b 100644 --- a/docs/source/contribute.md +++ b/docs/source/contribute.md @@ -43,6 +43,14 @@ All wrappers and OSP-core are part of a common directory structure: The changes should be clearly explained in the issue/Pull Request. - Once the features for a release have been reached, `dev` will be merged to `master/main`, every commit in the `master/main` branch theoretically being a new release. +In the next image it can be seen how the branches usually look during this workflow, and the different commits used to synchronise them: + +
+ +![](./_static/img/branch_workflow.png "Branches and commits") + +
+ ## Coding ### Documenting - All code must be properly documented with meaningful comments. diff --git a/docs/source/fundamentals.md b/docs/source/fundamentals.md new file mode 100644 index 0000000..743826e --- /dev/null +++ b/docs/source/fundamentals.md @@ -0,0 +1,264 @@ +# Fundamental concepts +In this section we will present some of the main concepts behind SimPhoNy. + +## General notions +### Degrees of interoperability +There is a multitude of tools and programs out there, all with their own formats and protocols. + +Every time a user wants to use one of these tools, they must familiarise themselves with the software. +Furthermore, if they want to integrate multiple tools in one workflow, the must, in most cases, +take care of the conversion on their own. + +Based on how tools communicate with other tools, we can define 3 levels: + +#### Compatibility + ```eval_rst + .. uml:: + :align: center + :caption: Compatibility + + rectangle A + rectangle B + rectangle C + rectangle D + + A <-> B + A <-[hidden]- C + C <-> D + ``` + + When we say two tools are compatible, they are able to communicate with each other + in a one to one basis. + This means the tools must either use the same format, or be able to convert to the format of the other. + + If we compare this to speaking languages, you could say `A` and `B`, or `C` and `D` speak the same language. + However, `A` has no way to talk with `C` or `D`, for example. + +#### De Facto Standard + ```eval_rst + .. uml:: + :align: center + :caption: De Facto Standard + + rectangle A + rectangle B + rectangle C + rectangle D + + A <--> B + A <-> C + C <-[hidden]- D + B <-[hidden]- D + A <-> D + ``` + + In this case, the level of operability is higher. + All tools know how to communicate with a tool whose format has become a de facto standard. + + To continue with our language simile, `A` would be a translator that speaks the languages of `B`, `C` and `D`. + If `B` wants to talk to `C`, they must first relay the message to `A`, + and `A` will convert it to a format that `C` understands. + +#### Interoperability + ```eval_rst + .. uml:: + :align: center + :caption: Interoperability + + usecase x as "open standard" + rectangle A + rectangle B + rectangle C + rectangle D + A <-down-> x + B <-right-> x + C <-left-> x + D <-up-> x + ``` + The highest level of operability is interoperability. + Here there is no need for all tools to go through the De Facto standard, + because there is a format that is known by all of them and enables all components to communicate among themselves. + + This final stage could be compared to all parties using an instant translator that can convert + text from one language into any other. + + +Interoperability between software tools is one of the most important objectives of the SimPhoNy framework. +### Semantic vs. syntactic + We can interpret a word as a specific sequence of characters without caring about the meaning itself. + This way, a simulation engine parsing an input file will know that the integer written after the keyword + `step` will be used to set the number of iterations the execution loop will run. + It does nothing else, and could as easily use the sequence `ppp`. + + However, for a person, the word `step` will be a sign representing a specific concept. + It could be the number of rounds in a simulation, but also the consecutive instructions in an algorithm, the + different levels in a stair or the motion a person makes when walking. + Based on the domain, a person can also list other relevant concepts and relationships + (e.g. when thinking of a stair, the `material` or the `width`). + + Being able to know the semantic meaning of an instance, and hence its connection to other concepts, + is one of the principles of SimPhoNy. For achieving this goal, ontologies play a major role. + +### Ontology +```eval_rst +.. important:: + An ontology is a formal specification of a shared conceptualization. `[Borst, 1997] + `_ . + +``` + +Let's look at the individual components of this definition, starting from the end. + - _Conceptualization_, an ontology will work on the ideas and relationships in an area of interest. + - _Shared_, the ideas and concepts are perceived and agreed by multiple people. + - _Specification_, it will define and describe them in detail, following some predetermined rules and format. + - _Formal_, meaning it will follow a machine readable syntax. + +In a simpler way, an ontology can be seen as the definition of concepts relevant to a given domain, +as well as the relationships between them, in a way that a machine can interpret it. + +For a deeper, more detailed analysis of the definition, refer to [[Guarino, 2009]](http://dx.doi.org/10.1007/978-3-540-92673-3_0). + +Ontologies are more elaborated than taxonomies in that they can include multiple kinds of relationships +(not just parent-child) between complex concepts in big domains. + +#### EMMO +The European Materials Modelling Ontology ([EMMO](https://github.com/emmo-repo/EMMO)) is an ontology developed by the European Materials Modelling Council ([EMMC](https://emmc.info/)). +EMMO's goal is to define a representational system universal for scientists in the field of materials modelling to enable interoperability. + +It has been designed from the bottom up, starting with the concepts of different domains and application fields +and generalising into a middle and top level layers, and it is currently being further +developed in multiple projects of the European Union. + +SimPhoNy is being developed with the intention of being compatible with EMMO, and an easy installation of the +ontology is available (further explained [here](./ontologies_included.md#working-with-emmo)). + +There is also [documentation](https://ontology.pages.fraunhofer.de/documentation/latest/) available for developing an EMMO compliant ontology (requires login). + +### CUDS +CUDS, or Common Universal Data Structure, is the ontology compliant data format of OSP-core: +- **CUDS is an ontology individual**: each CUDS object is an instantiation of a class in the ontology. + If we assume a food ontology that describes classes like pizza or pasta, a CUDS object could represent one specific pizza or pasta dish, that exists in the real world. + Similar to ontology individuals, CUDS objects can be related with other individuals/CUDS by relations defined in the ontology. Like a _pizza_ that 'hasPart' _tomato sauce_ +- **CUDS is API**: To allow users to interact with the ontology individuals and their data, CUDS provide a CRUD API. +- **CUDS is a container**: Depending on the relationship connecting two CUDS objects, a certain instance can be seen as a container of other instances. + We call a relationship that express containment an 'active relationship'. + In the pizza example, 'hasPart' would be an 'active relationship'. If one would like to share the pizza CUDS object with others, one would like to share also the tomato sauce. +- **CUDS is RDF**: Internally a CUDS object is only an interface to an RDF-based triple store that contains the data of all CUDS objects. +- **CUDS is a node in a graph**: : CUDS being individuals in an RDF graph implies that each CUDS object can also be seen as a node in a graph. + This does not conflict with the container perspective, instead we see it as to different views on the data. +## Technologies and frameworks +### RDF +[RDF](https://www.w3.org/RDF/) (Resource Description Framework) is a formal language for describing structured information +used in the Semantic Web. Its first specification was published in 1999 and extended in 2004. + +Knowledge is represented in directed graphs where the nodes are either ontological classes, +instances of those classes or literals and the edges the relationships connecting them. + +The graph is serialised in the form of triples of the form "subject-predicate-object" +- _Subject_: The IRI of the entity the triple refers to. + Blank nodes have no IRI, but they are outside of the scope of this thesis. +- _Predicate_: IRI of the relationship from subject to object. +- _Object_: Literal or IRI of an entity + +The following is an example of an RDF triple. This example will also be used to show the different serialisation formats of RDF. +For the IRIs, `dbpedia`'s namespace was used. +```eval_rst +.. uml:: + :align: center + :caption: RDF triple sample + + (dbr:J._R._R._Tolkien) as tolkien + (dbr:The_Lord_of_the_Rings) as lotr + lotr -> tolkien : dbo:author +``` + +The most used formats for storing RDF data are: +- [XML](https://www.w3.org/2001/sw/RDFCore/TR/WD-rdf-syntax-grammar-20030117/): + Historically the most common format given the amount of libraries for handling it. + It was released hand in hand with the RDF specification. + Unfortunately, XML is best used with tree-like structures rather than graphs, + which also makes it harder for humans to read. + + The example triple in XML is: + ```xml + + + + + + + + ``` + +- [N3](https://www.w3.org/TeamSubmission/n3/): Notation3 is designed with human readability as a motivator. + The RDF triples are written one per line, with the possibility to define common prefixes + and other directives for simplicity. + + The previous example in N3 would be: + ```turtle + @prefix dbo: . + @prefix dbr: . + dbr:The_Lord_of_the_Rings dbo:author dbr:J._R._R._Tolkien . + ``` + +- [Turtle](https://www.w3.org/TR/turtle/): Based on N3, it strips some of its syntax, making it easier to parse + for machines. + The recurring example would be exactly the same in Turtle as in N3. + +- [N-Triples](https://www.w3.org/TR/n-triples/): N-Triples are even simpler, without any of the syntactic sugar from N3 or Turtle. + The triples are written one per line without prefixes. This makes it a very easy format to parse + but complex to maintain/read by a human. + + The following representation should be in one line (it has been split for readability) + ```xml + + + . + ``` + +- [JSON-LD](https://www.w3.org/TR/json-ld/): uses the commonly accepted web data scheme for serialising RDF triples. + Easier than XML for humans, JSON has standard libraries in practically all programming languages. + + The example in JSON is: + ```json + {"@id": "http://dbpedia.org/resource/The_Lord_of_the_Rings", + "http://dbpedia.org/property/author": + [{"@id": "http://dbpedia.org/resource/J._R._R._Tolkien"}] + } + ``` +SimPhoNy supports all the previous formats (plus a simpler custom YAML) as inputs in the ontology installation. + +#### SPARQL +[SPARQL](https://www.w3.org/TR/sparql11-overview/) (recursively SPARQL Protocol and RDF Query Language) is the most common query language for RDF. +Queries are graph patterns (similar to the triples of Turtle) with variables for the parts of the pattern that make up the result. + +Variables start with the identifier `?` and represent concrete values that will be matched in the query process. +They can appear in multiple locations in the patterns and those present in the +`SELECT` clause will be returned as the query result. + +The query for the author of _The Lord of the Rings_ from our sample triples in SPARQL is: + ```sparql + PREFIX dbo: + PREFIX dbr: + SELECT ?person WHERE { + dbr:The_Lord_of_the_Rings dbo:author ?person . + } + ``` + +The SPARQL query language offers multiple types of result sets and clauses, most of which won't be used for this Master's thesis. +One which should be mentioned is the `FILTER` keyword. +This will limit the result to those that evaluate `true` to the expression inside the brackets. +For instance (omitting the prefix declaration for simplicity): + + ```sparql + SELECT ?character WHERE { + ?character dbp:affiliation dbr:The_Lord_of_the_Rings . + ?character dbo:age ?age . + FILTER(?age >= 100) + } + ``` +The previous query would return the characters from the book series with an age higher or equal to 100. +(Note that while the query is correct, the result is empty, as such information is not stored on DBpedia). + +For a very interesting and comprehensive introduction into RDF and SPARQL, see [[Hitzler, 2009]](http://dx.doi.org/10.1201/9781420090512). diff --git a/docs/source/getting_started.md b/docs/source/general_architecture.md similarity index 74% rename from docs/source/getting_started.md rename to docs/source/general_architecture.md index 1212794..d78de8b 100644 --- a/docs/source/getting_started.md +++ b/docs/source/general_architecture.md @@ -95,6 +95,9 @@ For that, a 3 layer schema is used: The closer to the user, the closer to the ontology concepts. The abstraction is replaced by specificity when you move towards the backend. +For example, the City, Street or Neighborhood classes from the demonstrative [City Ontology](./ontologies_included.md#the-city-ontology) included in OSP-core, as well as the individuals that can be instantiated using them, would be part of the semantic layer. Any wrapper (e.g. the included [SQLite wrapper](https://github.com/simphony/osp-core/tree/master/osp/wrappers/sqlite)), would be part of the interoperability layer. Finally, following the SQLite example, the [sqlite3 library](https://docs.python.org/3/library/sqlite3.html) from python would be part of the syntactic layer. + + For a full explanation on the architecture and design, go to [detailed design](./detailed_design.md). ## OSP-core @@ -199,81 +202,10 @@ other_entity = another_namespace.SomeOtherEntity() ``` ### Sessions -The sessions are the interoperability classes that connect to where the data is stored. -In the case of wrappers, they take care of keeping consistency between the backends (e.g. databases) and the internal registry. - -When you add an object to a wrapper, a copy of the object is created in the registry belonging to the session of the wrapper. - -To simplify the understanding and development of session classes, we have created a hierarchy: - -```eval_rst -.. uml:: - :caption: Simplified session inheritance scheme - :align: center - - rectangle "OSP-core" as OSP { - abstract class Session { - } - - class CoreSession implements Session { - } - - abstract class WrapperSession extends Session { - } - - class TransportSession implements WrapperSession { - } - - abstract class DbWrapperSession extends WrapperSession { - commit() - } - - abstract class SqlWrapperSession extends DbWrapperSession { - } - - abstract class SimWrapperSession extends WrapperSession { - run() - } - } - - rectangle "Sqlite wrapper" as sqlite { - class SqliteWrapperSession implements SqlWrapperSession { - } - } - - rectangle "SqlAlchemy wrapper" as sqlalchemy { - class SqlAlchemyWrapperSession implements SqlWrapperSession { - } - } - - rectangle "Simlammps wrapper" as simlammps { - class SimlammpsSession implements SimWrapperSession { - } - } - - ' ----------------------- - ' -------- NOTES -------- - ' ----------------------- - note as OSP.note_core - The CoreSession is the default - shared session for all Python objects - end note - OSP.note_core .. CoreSession - - note as note_db - The db changes are persisted via - cuds_object.session.commit() - end note - note_db .. DbWrapperSession - - note as OSP.note_sim - The simulation is run by calling - cuds_object.session.run() - end note - OSP.note_sim .. SimWrapperSession -``` +The [sessions](./detailed_design.md#session) are the interoperability classes that connect to where the data is stored. +[In the case of wrappers](./wrapper_development.md#coding), they take care of keeping consistency between the backends (e.g. databases) and the internal registry. -As you can see, CoreSession is the default one used when instantiating a new object in your workspace. +The `CoreSession` is the default one used when instantiating a new object in your workspace. When you add an object to a wrapper, a copy of the object is created in the registry belonging to the session of the wrapper. ## Wrappers Like we have mentioned in previous sections, wrappers allow the user to interact diff --git a/docs/source/index.md b/docs/source/index.md index 3508590..68c2045 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -1,63 +1,125 @@ -# Welcome to SimPhoNy docs! -Here you can browse though the general documentation of SimPhoNy. +# Welcome to the SimPhoNy docs! -**Compatibility matrix** +SimPhoNy is an ontology-based [open-source](./license.md) Python framework that promotes and enables interoperability between any 3rd-party software tool. Here you can learn more about it. ```eval_rst - ============= ========== - SimPhoNy docs OSP-core - ============= ========== - 2.3.x 3.4.0-beta - 2.2.x 3.3.5-beta - 2.1.x 3.3.0-beta - ============= ========== -``` -```eval_rst +.. panels:: + :body: text-center + + --- + **Getting Started** + + Overview, main concepts, and installation guide + + .. link-button:: overview.html + :text: To the getting started guides + :classes: btn-outline-primary stretched-link + + --- + **Usage Guide** + + Core functionalities, wrappers and advanced utilities + + .. link-button:: jupyter/cuds_api.ipynb + :text: To the usage guides + :classes: btn-outline-primary stretched-link + + --- + + **Working with Ontologies** + + Ontology overview, included ontologies, YAML ontologies and ontology querying + + .. link-button:: ontology_intro.html + :text: To the ontology guides + :classes: btn-outline-primary stretched-link + --- + + **Wrapper Development** + + A deep dive into the wrapper mechanism for developing new wrappers + + .. link-button:: wrapper_development.html + :text: To the wrapper development guides + :classes: btn-outline-primary stretched-link + --- + + **API Reference** + + Python API of CUDS, the *Session* classes, and other utilities + + .. link-button:: api_ref.html + :text: To the API reference + :classes: btn-outline-primary stretched-link + + --- + **Additional Information** + + License, acknowledgements, data protection, contact info and more + + .. link-button:: contribute.html + :text: To get more information + :classes: btn-outline-primary stretched-link + .. toctree:: - :caption: Introduction + :hidden: + :caption: Getting Started :maxdepth: 2 - about.md - motivation.md - getting_started.md + overview.md + fundamentals.md + general_architecture.md installation.md - jupyter/cuds-api.ipynb + +.. toctree:: + :hidden: + :caption: Usage Guide + :maxdepth: 2 + + jupyter/cuds_api.ipynb jupyter/sessions_and_vars.ipynb - detailed_design.md utils.md + jupyter/multiple_wrappers.ipynb + jupyter/simlammps.ipynb + jupyter/quantum_espresso.ipynb .. toctree:: - :caption: Ontology + :hidden: + :caption: Working with Ontologies :maxdepth: 2 ontology_intro.md ontologies_included.md yaml.md owl.md - jupyter/ontology-interface.ipynb - + jupyter/ontology_interface.ipynb .. toctree:: - :caption: Wrappers + :hidden: + :caption: Wrapper Development + :maxdepth: 2 - jupyter/multiple-wrappers.ipynb - jupyter/simlammps.ipynb - jupyter/quantum-espresso.ipynb wrapper_development.md jupyter/wrapper_development.ipynb +.. toctree:: + :hidden: + :caption: API Reference + :maxdepth: 2 + + api_ref.md .. toctree:: - :caption: Additional + :hidden: + :caption: Additional Info :maxdepth: 2 contribute.md - api_ref.md - json.md + detailed_design.md links.md + license.md Data protection Imprint contact.md - ``` diff --git a/docs/source/json.md b/docs/source/json.md deleted file mode 100644 index 2f0095e..0000000 --- a/docs/source/json.md +++ /dev/null @@ -1,16 +0,0 @@ - - -# Serialization JSON schema of CUDS objects - -When you serialize a CUDS object using the -[`serialize()` method in the utils module](api_ref.html#osp.core.utils.general.serialize), -you will get a json document as a result. -The method will traverse the hierarchical datastructure -using Depth First Traversal. -Therefore, its result is a json array composed of several flat CUDS objects. - -This array can later be deserialized using the opposite -[`deserialize`](api_ref.html#osp.core.utils.general.deserialize). - -The serialization is done via [JSON-LD](https://json-ld.org/), -with the schema used for the [OSP API in Marketplace](https://gitlab.cc-asp.fraunhofer.de/MarketPlace/osp-api). diff --git a/docs/source/jupyter/cuds-api.ipynb b/docs/source/jupyter/cuds-api.ipynb deleted file mode 100644 index 7eb35a9..0000000 --- a/docs/source/jupyter/cuds-api.ipynb +++ /dev/null @@ -1,295 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tutorial: CUDS API\n", - "\n", - "This tutorial introduces the CUDS API.\n", - "The code given here is based on [this](https://github.com/simphony/osp-core/blob/master/examples/api_example.py) example." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Background\n", - "\n", - "CUDS stands for Common Universal Data Structure, and it is used to uniformly represent an ontological individual. In the python implementation of OSP-core, it means that every ontological individual is an instance of the `Cuds` class.\n", - "\n", - "Conceptually, we consider CUDS objects as containers. The content of a CUDS object consists of other CUDS objects. This means that a CUDS is a [recursive data structure](https://en.wikipedia.org/wiki/Recursive_data_type). There are various types of relationships between a container and its contained objects. We will see this later on.\n", - "\n", - "As a data structure, CUDS exposes an API that provides the functionalities *Create*, *Read*, *Update* and *Delete* (or simply [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)).\n", - "Except *Create*, all the other functionalities are supported by the methods `add`, `get`, `remove`, `update` and `iter`, that are defined in the class `Cuds`. Here, we cover all of the methods except `update` since it is used to synchronize between two or more data sources, and in this tutorial we will only use one, that is the data we will create through our python code on the fly. Check out the [wrapper tutorial](multiple-wrappers.ipynb) to see the `update` method in action.\n", - "\n", - "Every CUDS object is related to an ontological concept via the ontological `is a` relation.\n", - "In the python implementation of OSP-core, all ontological concepts are instances of the `OntologyEntity` class.\n", - "The ontological concept that can be used to *Create* CUDS objects are instances of `OntologyClass`, a subclass of `OntologyEntity`.\n", - "The ontological concepts are organized in namespaces." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Let's get hands on\n", - "We start by importing the example namespace from OSP-core. It consists of concepts that were automatically generated from the [dummy city ontology](https://github.com/simphony/osp-core/blob/master/osp/core/ontology/docs/city.ontology.yml). There is [another tutorial](#) on the ontology YAML file. However, it is not important for the purposes of this tutorial.\n", - "\n", - "Before we can start, you have to install the city ontology. Use out tool pico for this:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "INFO 2020-08-06 08:03:23,850 [osp.core.ontology.installation]: Will install the following namespaces: ['city']\nINFO 2020-08-06 08:03:23,880 [osp.core.ontology.yml.yml_parser]: Parsing YAML ontology file /mnt/c/Users/urba/Desktop/repos/osp-core/osp/core/ontology/docs/city.ontology.yml\nINFO 2020-08-06 08:03:23,892 [osp.core.ontology.yml.yml_parser]: You can now use `from osp.core.namespaces import city`.\nINFO 2020-08-06 08:03:23,892 [osp.core.ontology.parser]: Loaded 367 ontology triples in total\nINFO 2020-08-06 08:03:23,927 [osp.core.ontology.installation]: Installation successful\n" - } - ], - "source": [ - "!pico install city" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you can start coding:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# If you just installed the ontology from within this notebook and this line doesn't work, please restart the kernel and run this cell again.\n", - "from osp.core.namespaces import city" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's create a CUDS object that represents the city of Freiburg:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "c = city.City(name=\"Freiburg\", coordinates=[47, 7])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The variable `c` is assigned with a newly created CUDS object. This object was initialized with a name (Freiburg) and coordinates (\\[47, 7\\]). To understand why these two arguments were necessary, we'll have to take a look at the ontology. This will be explained in another tutorial." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each CUDS object has a unique identifier (UID) which can be accessed:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "uid of c: 34ffc443-9287-46de-b2aa-477a57f68a2f\n" - } - ], - "source": [ - "print(\"uid of c: \" + str(c.uid))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The type of an object can be queried as well:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "type of c: city.City\n" - } - ], - "source": [ - "print(\"type of c: \" + str(c.oclass))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's add two citizens to our city object:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": "" - }, - "metadata": {}, - "execution_count": 6 - } - ], - "source": [ - "p1 = city.Citizen(name=\"Peter\")\n", - "p2 = city.Citizen(name=\"Anne\")\n", - "c.add(p1, rel=city.hasInhabitant)\n", - "c.add(p2, rel=city.hasInhabitant)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the relationship type between the city and its two citizens in this case is 'HAS_INHABITANT'. In our context, this means that Anne and Peter are Freiburg inhabitants." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we would like to iterate over the objects contained in the city object. We do so by using the `iter()` function:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "uid: 703ec4ba-e89b-4e79-8985-5b6d31223328\nuid: 088c08f9-7961-4586-b908-23170fb0f059\n" - } - ], - "source": [ - "for el in c.iter():\n", - " print(\"uid: \" + str(el.uid))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can `get()` an object from a container if we have a UID of one of its immediate contained objects:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "city.Citizen: 703ec4ba-e89b-4e79-8985-5b6d31223328\n" - } - ], - "source": [ - "print(c.get(p1.uid))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also filter the contained objects by type: " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "[, ]\n" - } - ], - "source": [ - "print(c.get(oclass=city.Citizen))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We remove objects using the `remove()` function:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "c.remove(p1)\n", - "# c.remove(p1.uid) also works!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's close this tutorial by adding some neighbourhoods in a loop:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(6):\n", - " c.add(city.Neighborhood(name=\"neighbourhood %s\" % i))" - ] - } - ], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/docs/source/jupyter/cuds_api.ipynb b/docs/source/jupyter/cuds_api.ipynb new file mode 100644 index 0000000..5fb9905 --- /dev/null +++ b/docs/source/jupyter/cuds_api.ipynb @@ -0,0 +1,617 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial: CUDS API\n", + "\n", + "
\n", + "\n", + "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/simphony/docs/master?filepath=docs%2Fsource%2Fjupyter%2Fcuds_api.ipynb \"Click to run the tutorial yourself!\")\n", + " \n", + "
\n", + "\n", + "This tutorial introduces the CUDS API. The code given here is based on [this](https://github.com/simphony/osp-core/blob/master/examples/api_example.py) example.\n", + "\n", + "Note that that this tutorial, as all others in this documentation, are Jupyter notebooks that can be downloaded by clicking on the \"Edit on Github\" button on the top right of the page." + ] + }, + { + "attachments": { + "1de166aa-9888-4fe1-9794-406012a2ebe4.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Background\n", + "\n", + "CUDS stands for Common Universal Data Structure, and it is used to uniformly represent ontology individuals. In the python implementation of OSP-core, it means that every ontology individual is an instance of the `Cuds` class.\n", + "\n", + "As every CUDS object is an ontology individual, each CUDS is related to an ontology class via the ontological `is a` relation, and can be connected to other CUDS objects through ontology relationships. In the python implementation of OSP-core, all such ontological classes are instances of the `OntologyClass` class, which is itself a subclass of `OntologyEntity`. The ontology entitites are organized in namespaces.\n", + "\n", + "In OSP-core, the ontology relationships can be tagged as active or passive relationships. This is done in the ontology installation file. Such feature lets CUDS objects act as containers, so that content of a CUDS object consists of other CUDS objects. This means that a CUDS is a [recursive data structure](https://en.wikipedia.org/wiki/Recursive_data_type). Such active and passive relationships are directed, meaning that they have a source and a target. If a CUDS is the source of a connection via an active relationship to another CUDS, then the former contains the latter. Conversely, if a passive relationship is used, then the latter is contained in the former. Untagged ontology relationships do not define any containment.\n", + "\n", + "**Note:** currently, each time a source CUDS object is connected to another target CUDS object through an active relationship, an inverse passive relationship is also created, connecting the target CUDS (acting as source) and the source CUDS (acting as target).\n", + "\n", + "
\n", + " \n", + "
Containment in CUDS objects. On the graph view of the left hand side, the arrows depict active relationships, while the segment depicts any other untagged ontology relationship. On the right hand side, a containment view is provided.
\n", + "
\n", + "\n", + "The most important functionalities that the CUDS data structure exposes as python methods are the following:\n", + "\n", + "- `add`: Connects the current CUDS object to one or more CUDS objects through a specific ontology relationship. If the chosen relationship is not an active relationship, one CUDS will not contain the other nor viceversa.\n", + "\n", + "- `remove`: Despite its name, it does NOT delete the CUDS object itself. Instead, it just disconnects the current CUDS object from one or more CUDS objects.\n", + "\n", + "- `get`: Returns the CUDS objects connected to the current CUDS object through a specific ontology relationship. \n", + "\n", + "- `iter`: Similar to the `get` method, it just returns one CUDS objects at a time instead of all at once (python [iterator](https://python.land/deep-dives/python-iterator)), so that memory can be saved.\n", + "\n", + "- `is_a`: Checks if the CUDS object is an instance of the given ontology class.\n", + "\n", + "In addition, other important functionalities are exposed as python properties:\n", + "\n", + "- `oclass`: The ontology class of the ontology individual represented by the CUDS object. If the individual belongs to multiple classes, only one of them is referenced by the property.\n", + "\n", + "- `uid`: A unique ID identifying an ontology individual.\n", + "\n", + "- `iri`: The [Internationalized Resource Identifier](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/) of the CUDS object. It consists of a CUDS namespace prefix and the unique ID of the CUDS object. This will be further clarified in the tutorial.\n", + "\n", + "- `attributes`: The values of the ontology attributes of an individual (also known as [data properties](https://www.w3.org/TR/owl2-syntax/#Data_Properties) in [OWL](https://en.wikipedia.org/wiki/Web_Ontology_Language)) may also be accessed and modified as python properties of the CUDS objects. For example: `cuds_object.name = \"Bob\"`.\n", + "\n", + "There are some advanced functionalities NOT covered in this tutorial. Among them, we highlight the `update` method, which is covered in the [wrapper tutorial](multiple-wrappers.ipynb), where it can be seen in action. For a complete list of available methods and properties, check the API reference. That whole set of methods and attributes constitutes the CUDS [API](https://en.wikipedia.org/wiki/API).\n", + "\n", + "[//]: # (TODO: Link to API reference.)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Let's get hands on\n", + "In this tutorial, we will work with the `city` namespace, the example namespace from OSP-core. It consists of concepts from the example [city ontology](https://github.com/simphony/osp-core/blob/master/osp/core/ontology/docs/city.ontology.yml).\n", + "\n", + "[//]: # (TODO: simple graphical representation of the city ontology.)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "The first step is to install the city ontology. Use the tool pico for this. If you want to know more about ontology installation, check the documentation on the [pico ontology installation tool](https://simphony.readthedocs.io/en/latest/utils.html#pico-installs-cuds-ontologies), [YAML ontology installation files](https://simphony.readthedocs.io/en/latest/yaml.html), and [installing OWL ontologies](https://simphony.readthedocs.io/en/latest/owl.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO 2021-04-01 13:40:41,433 [osp.core.ontology.installation]: Will install the following namespaces: ['city']\n", + "INFO 2021-04-01 13:40:41,448 [osp.core.ontology.yml.yml_parser]: Parsing YAML ontology file /home/jose/.local/lib/python3.9/site-packages/osp/core/ontology/docs/city.ontology.yml\n", + "INFO 2021-04-01 13:40:41,476 [osp.core.ontology.yml.yml_parser]: You can now use `from osp.core.namespaces import city`.\n", + "INFO 2021-04-01 13:40:41,476 [osp.core.ontology.parser]: Loaded 202 ontology triples in total\n", + "INFO 2021-04-01 13:40:41,491 [osp.core.ontology.installation]: Installation successful\n" + ] + } + ], + "source": [ + "!pico install city" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then you can import the `city` namespace." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# If you just installed the ontology from within this notebook and this line doesn't work, please restart the kernel and run this cell again.\n", + "from osp.core.namespaces import city" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You are now creating some CUDS objects that you are going to use to try out the functionalities of the CUDS data structure." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "c = city.City(name=\"Freiburg\", coordinates=[47, 7]) # Ontology individual representing the city of Freiburg.\n", + "p1 = city.Citizen(name=\"Peter\") # Ontology indidual representing a specific person, \"Peter\".\n", + "p2 = city.Citizen(name=\"Anne\") # Ontology individual representing another specific person, \"Anne\"." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The names `c, p1, p2` are assigned to the newly created CUDS objects. The keyword arguments `name` and `coordinates` let you directly assign values for such ontology attributes (also known as [data properties](https://www.w3.org/TR/owl2-syntax/#Data_Properties) in [OWL](https://en.wikipedia.org/wiki/Web_Ontology_Language)) to the new CUDS objects. The available ontology attributes for each ontology class depend on the specific class being instantiated. For example, the ontology attribute `name` is available for both the _City_ and the _Citizen_ ontology classes in the sample _City_ ontology. The attribute `coordinates` is available for the _City_ ontology class, but not for the _Citizen_ class." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "### Functionalities exposed as python properties" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each CUDS object has a unique identifier (UID), which can be accessed using the `uid` property:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid of c: e0b721ae-6004-4834-80f1-e6e979952d1f\n", + "uid of p1: 63874785-0de6-43ad-8669-999482501ad1\n", + "uid of p2: 39c7334f-ef62-4dbc-ae2e-390d7f3ca641\n" + ] + } + ], + "source": [ + "print(\"uid of c: \" + str(c.uid))\n", + "print(\"uid of p1: \" + str(p1.uid))\n", + "print(\"uid of p2: \" + str(p2.uid))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "Similarly, each CUDS object has an [IRI](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/), which serves to reference it in the [Semantic Web](https://en.wikipedia.org/wiki/Semantic_Web) and improves the compatibility of the CUDS format with the [Resource Description Framework](https://en.wikipedia.org/wiki/Resource_Description_Framework) data model. Note that the IRI of each CUDS object contains its unique identifier." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IRI of c: http://www.osp-core.com/cuds#e0b721ae-6004-4834-80f1-e6e979952d1f\n", + "IRI of p1: http://www.osp-core.com/cuds#63874785-0de6-43ad-8669-999482501ad1\n", + "IRI of p2: http://www.osp-core.com/cuds#39c7334f-ef62-4dbc-ae2e-390d7f3ca641\n" + ] + } + ], + "source": [ + "print(\"IRI of c: \" + str(c.iri))\n", + "print(\"IRI of p1: \" + str(p1.iri))\n", + "print(\"IRI of p2: \" + str(p2.iri))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The class of the ontology individual represented by the CUDS object can be queried as well:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "oclass of c: city.City\n", + "oclass of p1: city.Citizen\n", + "oclass of p2: city.Citizen\n" + ] + } + ], + "source": [ + "print(\"oclass of c: \" + str(c.oclass))\n", + "print(\"oclass of p1: \" + str(p1.oclass))\n", + "print(\"oclass of p2: \" + str(p2.oclass))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `uid`, `iri` and `oclass` properties **cannot be modified**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "Finally, the values of the ontology attributes of an individual can be easily **accessed and modified** using the dot notation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name of c: Freiburg. Coordinates of c: [47 7].\n", + "Name of p1: Peter\n", + "Name of p2: Anne\n", + "\n", + "Change the name of Peter.\n", + "Name of p1: Bob.\n" + ] + } + ], + "source": [ + "print(f\"Name of c: {c.name}. Coordinates of c: {c.coordinates}.\" )\n", + "print(\"Name of p1: \" + str(p1.name))\n", + "print(\"Name of p2: \" + str(p2.name))\n", + "\n", + "print(f\"\\nChange the name of {p1.name}.\")\n", + "p1.name = \"Bob\"\n", + "print(f\"Name of p1: {p1.name}.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "### Functionalities exposed as python methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we may connect the two citizens to our city object:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.add(p1, rel=city.hasInhabitant)\n", + "c.add(p2, rel=city.hasInhabitant)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the relationship type between the city and its two citizens in this case is 'hasInhabitant'. In our context, this means that Anne and Peter are Freiburg inhabitants. Moreover, in the [city ontology](https://github.com/simphony/osp-core/blob/master/osp/core/ontology/docs/city.ontology.yml), this relationship is defined as an active relationship. This means that Anne and Peter are not only connected to Freiburg, but are also contained in the Freiburg CUDS object." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we would like to iterate over the objects contained in the city object. We do so by using the `iter` function:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: 63874785-0de6-43ad-8669-999482501ad1\n", + "uid: 39c7334f-ef62-4dbc-ae2e-390d7f3ca641\n" + ] + } + ], + "source": [ + "for el in c.iter():\n", + " print(\"uid: \" + str(el.uid))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can `get` a target object from a CUDS object if we have a UID of one of its immediate contained objects. This will not work if the target object is not contained in the CUDS object, but just connected to it." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "city.Citizen: 63874785-0de6-43ad-8669-999482501ad1\n", + "None\n" + ] + } + ], + "source": [ + "print(c.get(p1.uid)) # `p1`is contained in `c` because they are connected through an active relationship.\n", + "print(p1.get(c.uid)) # `c` is connected to `p1`, but it is NOT contained in `p1`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also filter the contained objects by type: " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[, ]\n" + ] + } + ], + "source": [ + "print(c.get(oclass=city.Citizen))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We remove objects using the `remove()` function. Despite its name, this just disconnects the target object from the CUDS, but does NOT delete the target object from the memory." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "city.Citizen: 63874785-0de6-43ad-8669-999482501ad1\n", + "None\n" + ] + } + ], + "source": [ + "c.remove(p1)\n", + "# c.remove(p1.uid) also works!\n", + "print(p1) # `p1` still exists,\n", + "print(c.get(p1.uid)) # but is no longer connected neither contained in `c`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's close this tutorial by adding some neighborhoods in a loop," + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(6):\n", + " c.add(city.Neighborhood(name=\"neighborhood %s\" % i))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "and then verifying that they are indeed neighborhoods, just to also try the `is_a` method.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(n.is_a(city.Neighborhood) for n in c.get(oclass=city.Neighborhood))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + } + }, + "source": [ + "The existing ontology individuals and the relationships among them have been depicted below, using the utility `pretty_print` (see the [Utilities section](https://simphony.readthedocs.io/en/latest/utils.html?highlight=pretty_print)). Note that some attributes that were not specified were set automatically to the default values specified in the ontology." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "nteract": { + "transient": { + "deleting": false + } + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "- Cuds object named :\n", + " uuid: e0b721ae-6004-4834-80f1-e6e979952d1f\n", + " type: city.City\n", + " superclasses: city.City, city.GeographicalPlace, city.PopulatedPlace, cuba.Entity\n", + " values: coordinates: [47 7]\n", + " description: \n", + " To Be Determined\n", + "\n", + " |_Relationship city.hasInhabitant:\n", + " | - city.Citizen cuds object named :\n", + " | uuid: 39c7334f-ef62-4dbc-ae2e-390d7f3ca641\n", + " | age: 25\n", + " |_Relationship city.hasPart:\n", + " - city.Neighborhood cuds object named :\n", + " . uuid: 79eec4f9-55c5-464d-b6c3-dc4382e55476\n", + " . coordinates: [0 0]\n", + " - city.Neighborhood cuds object named :\n", + " . uuid: 50fa8dac-2f5f-4b2c-ab8a-24f7f1b41f2c\n", + " . coordinates: [0 0]\n", + " - city.Neighborhood cuds object named :\n", + " . uuid: f6c20bfa-c29b-46bb-a5f5-8bc88b3e3621\n", + " . coordinates: [0 0]\n", + " - city.Neighborhood cuds object named :\n", + " . uuid: e161ea1c-40d8-4556-93ad-dfd4d178d669\n", + " . coordinates: [0 0]\n", + " - city.Neighborhood cuds object named :\n", + " . uuid: a3af8db2-28cd-457b-84ed-b420be5212c2\n", + " . coordinates: [0 0]\n", + " - city.Neighborhood cuds object named :\n", + " uuid: 8de47383-4416-44be-99dc-e5903b1d14dd\n", + " coordinates: [0 0]\n" + ] + } + ], + "source": [ + "from osp.core.utils import pretty_print\n", + "pretty_print(c)" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/docs/source/jupyter/multiple-wrappers.ipynb b/docs/source/jupyter/multiple_wrappers.ipynb similarity index 98% rename from docs/source/jupyter/multiple-wrappers.ipynb rename to docs/source/jupyter/multiple_wrappers.ipynb index 881309a..40ae201 100644 --- a/docs/source/jupyter/multiple-wrappers.ipynb +++ b/docs/source/jupyter/multiple_wrappers.ipynb @@ -353,17 +353,7 @@ "source": [] } ], - "metadata": { - "kernelspec": { - "name": "python3", - "display_name": "Python 3.7.5 64-bit", - "metadata": { - "interpreter": { - "hash": "6b8ae71b8e061ba9b211c188436d81117d4fa8435b658f808f738c6c9c6d5c29" - } - } - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 2 } \ No newline at end of file diff --git a/docs/source/jupyter/ontology-interface.ipynb b/docs/source/jupyter/ontology-interface.ipynb deleted file mode 100644 index e3845ed..0000000 --- a/docs/source/jupyter/ontology-interface.ipynb +++ /dev/null @@ -1,412 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tutorial: Ontology Interface\n", - "\n", - "This tutorial introduces the interface to the installed ontologies. The code presented is based on [this example](#). If you want to create your own ontology please refer to [this guide](../yaml.md)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Background" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the previous tutorial we have discussed the CUDS API. CUDS objects correspond to ontology individuals. In this tutorial we present the API of all other entities: ontology classes, relationships and attributes. These are defined in an ontology file in [YAML](../yaml.md) or [OWL](../owl.md) format. With the presented API you can access the entities and navigate within an ontology." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Preliminaries" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "INFO 2020-12-02 13:53:47,242 [osp.core.ontology.installation]: Will install the following namespaces: ['city']\n", - "INFO 2020-12-02 13:53:47,288 [osp.core.ontology.yml.yml_parser]: Parsing YAML ontology file /mnt/c/Users/dea/Documents/Projects/simphony/osp-core/osp/core/ontology/docs/city.ontology.yml\n", - "INFO 2020-12-02 13:53:47,357 [osp.core.ontology.yml.yml_parser]: You can now use `from osp.core.namespaces import city`.\n", - "INFO 2020-12-02 13:53:47,358 [osp.core.ontology.parser]: Loaded 403 ontology triples in total\n", - "INFO 2020-12-02 13:53:47,397 [osp.core.ontology.installation]: Installation successful\n" - ] - } - ], - "source": [ - "# Install the ontology\n", - "!pico install city" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Accessing entities\n", - "\n", - "First, we want to acces the entities in the ontology. For that, we need to know the installed ontology namespaces. The namespace is stated at the top of the ontology YAML files:\n", - "\n", - "```yaml\n", - "---\n", - "version: \"0.0.3\"\n", - "\n", - "namespace: \"city\"\n", - "\n", - "ontology:\n", - " ...\n", - "```\n", - "\n", - "Alternatively you can use pico to see the installed namespaces:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Packages:\n\t- simlammps_ontology\n\t- city\nNamespaces:\n\t- xml\n\t- rdf\n\t- rdfs\n\t- xsd\n\t- cuba\n\t- ns1\n\t- owl\n\t- simlammps_ontology\n\t- city\n" - ] - } - ], - "source": [ - "!pico list" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Knowing the name of the namespace, we can import it in python. With the namespace python object we can access the entities within the namespace:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can use the namespace to access its entities\ncity.LivingBeing\n\nYou can also use index notation. For owl ontologies, this queries the entities by rdf.label.It returns a list of all entities with the same label\nTrue\n\nYou can access the namespace of an entity\nFalse\n" - ] - } - ], - "source": [ - "from osp.core.namespaces import city\n", - "\n", - "print(\"\\nYou can use the namespace to access its entities\")\n", - "print(city.LivingBeing)\n", - "\n", - "print(\"\\nYou can also use index notation. \"\n", - " \"For owl ontologies, this queries the entities by rdf.label.\"\n", - " \"It returns a list of all entities with the same label\")\n", - "print(city.LivingBeing == city[\"LivingBeing\"][0])\n", - "\n", - "print(\"\\nYou can access the namespace of an entity\")\n", - "print(city is city.LivingBeing.namespace)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Accessing super- and subclasses\n", - "\n", - "Using the properties `superclasses` and `subclasses` it is easy to navigate the ontology. Direct superclasses and subclasses can also be accessed:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can access the superclasses and the subclasses\n{, }\n{, , }\n\nYou can access the direct superclasses and subclasses\n{}\n{}\n\nYou can access a description of the entities\nA being that lives\n\nYou can test if one entity is a subclass / superclass of another\nTrue\nTrue\n" - ] - } - ], - "source": [ - "print(\"\\nYou can access the superclasses and the subclasses\")\n", - "print(city.LivingBeing.superclasses)\n", - "print(city.LivingBeing.subclasses)\n", - "\n", - "print(\"\\nYou can access the direct superclasses and subclasses\")\n", - "print(city.LivingBeing.direct_superclasses)\n", - "print(city.LivingBeing.direct_subclasses)\n", - "\n", - "print(\"\\nYou can access a description of the entities\")\n", - "print(city.LivingBeing.description)\n", - "\n", - "print(\"\\nYou can test if one entity is a subclass / superclass of another\")\n", - "print(city.Person.is_subclass_of(city.LivingBeing))\n", - "print(city.LivingBeing.is_superclass_of(city.Person))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Access entities using a string\n", - "\n", - "Sometimes you only have a string refering to an entity. Using the `get_entity` function you can get the corresponding python object easily:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can get an entity with a string\ncity.LivingBeing\nTrue\n" - ] - } - ], - "source": [ - "from osp.core.namespaces import get_entity # noqa: E402\n", - "\n", - "print(\"\\nYou can get an entity with a string\")\n", - "print(get_entity(\"city.LivingBeing\"))\n", - "print(get_entity(\"city.LivingBeing\") == city.LivingBeing)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Testing the type of the entities\n", - "\n", - "In the OSP-core YAML ontology files three types of entities can be defined: classes, relationships and attributes. Relationships are subclasses of CUBA.RELATIONSHIP and attributes are subclasses of CUBA.ATTRIBUTE. There are different Python objects for the different entity types. You can use both to check which type of entity you are dealing with:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can test if an entity is a class\nTrue\nTrue\n\nYou can test if an entity is a relationship\nTrue\nTrue\n\nYou can test if an entity is a attribute\nTrue\nTrue\n" - ] - } - ], - "source": [ - "from osp.core.namespaces import cuba # noqa: E402\n", - "\n", - "# These are the classes for the ontology entities\n", - "from osp.core.ontology import ( # noqa: F401, E402\n", - " OntologyEntity,\n", - " OntologyClass,\n", - " OntologyRelationship,\n", - " OntologyAttribute\n", - ")\n", - "\n", - "print(\"\\nYou can test if an entity is a class\")\n", - "print(isinstance(city.LivingBeing, OntologyClass))\n", - "print(not city.LivingBeing.is_subclass_of(cuba.relationship)\n", - " and not city.LivingBeing.is_subclass_of(cuba.attribute))\n", - "\n", - "print(\"\\nYou can test if an entity is a relationship\")\n", - "print(isinstance(city.hasInhabitant, OntologyRelationship))\n", - "print(city.hasInhabitant.is_subclass_of(cuba.relationship))\n", - "\n", - "print(\"\\nYou can test if an entity is a attribute\")\n", - "print(isinstance(city.name, OntologyAttribute))\n", - "print(city.name.is_subclass_of(cuba.attribute))\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operations specific to ontology classes\n", - "\n", - "The different types of entities differ in the operations they offer. For classes, you can access the attributes:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can get the attributes of an ontology class and their defaults\n{: (rdflib.term.Literal('John Smith'), False, None), : (rdflib.term.Literal('25', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')), False, None)}\n\nYou can get the non-inherited attributes and their defaults\n{}\n{: (rdflib.term.Literal('John Smith'), False, None), : (rdflib.term.Literal('25', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')), False, None)}\n" - ] - } - ], - "source": [ - "print(\"\\nYou can get the attributes of an ontology class and their defaults\")\n", - "print(city.Citizen.attributes)\n", - "\n", - "print(\"\\nYou can get the non-inherited attributes and their defaults\")\n", - "print(city.Citizen.own_attributes)\n", - "print(city.LivingBeing.own_attributes)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operations specific to ontology relationships\n", - "\n", - "You can access the inverse, the domain and the range of relationships." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can get the inverse of a relationship\ncity.INVERSE_OF_hasInhabitant\n" - ] - } - ], - "source": [ - "print(\"\\nYou can get the inverse of a relationship\")\n", - "print(city.hasInhabitant.inverse)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operations specific to attributes\n", - "\n", - "You can acces the datatype and the argument name of attributes." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\nYou can get the argument name of an attribute. The argument name is used when instantiating CUDS objects\nage\n\nYou can get the datatype of attributes\nhttp://www.w3.org/2001/XMLSchema#integer\n\nYou can use the attribute to convert values to the datatype of the attribute\n10\n" - ] - } - ], - "source": [ - "print(\"\\nYou can get the argument name of an attribute. \"\n", - " \"The argument name is used when instantiating CUDS objects\")\n", - "print(city.age.argname)\n", - "\n", - "print(\"\\nYou can get the datatype of attributes\")\n", - "print(city.age.datatype)\n", - "\n", - "print(\"\\nYou can use the attribute to convert values \"\n", - " \"to the datatype of the attribute\")\n", - "print(city.age.convert_to_datatype(\"10\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## CUDS\n", - "\n", - "You can use ontology classes to create CUDS objects." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": "\nYou can instantiate CUDS objects using ontology classes\ncity.Citizen: 1714a778-5943-4611-af3a-e3b0cb32693d\nTake a look at api_example.py for a description of the CUDS API\n\nYou can check if a CUDS object is an instace of a ontology class\nTrue\nTrue\n\nYou can get the ontology class of a CUDS object.\ncity.Citizen\n" - } - ], - "source": [ - "print(\"\\nYou can instantiate CUDS objects using ontology classes\")\n", - "print(city.Citizen(name=\"Test Person\", age=42))\n", - "print(\"Take a look at api_example.py for a description of the CUDS API\")\n", - "\n", - "print(\"\\nYou can check if a CUDS object is an instace of a ontology class\")\n", - "print(city.Citizen(name=\"Test Person\", age=42).is_a(city.Citizen))\n", - "print(city.Citizen(name=\"Test Person\", age=42).is_a(city.LivingBeing))\n", - "\n", - "print(\"\\nYou can get the ontology class of a CUDS object.\")\n", - "print(city.Citizen(name=\"Test Person\", age=42).oclass)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "name": "python3", - "display_name": "Python 3.7.5 64-bit", - "metadata": { - "interpreter": { - "hash": "6b8ae71b8e061ba9b211c188436d81117d4fa8435b658f808f738c6c9c6d5c29" - } - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/docs/source/jupyter/ontology_interface.ipynb b/docs/source/jupyter/ontology_interface.ipynb new file mode 100644 index 0000000..9529d98 --- /dev/null +++ b/docs/source/jupyter/ontology_interface.ipynb @@ -0,0 +1,923 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial: Ontology interface\n", + "\n", + "
\n", + "\n", + "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/simphony/docs/master?filepath=docs%2Fsource%2Fjupyter%2Fontology_interface.ipynb \"Click to run the tutorial yourself!\")\n", + " \n", + "
\n", + "\n", + "\n", + "This tutorial introduces the interface to the installed ontologies. The code presented is based on [this example](https://github.com/simphony/osp-core/blob/master/examples/ontology_example.py)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Background" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In an ontological framework, ontology entities are used as a knowledge representation form. Those can be further categorized in two groups: ontology individuals ([assertional knowledge](https://en.wikipedia.org/wiki/Abox)), and ontology classes, relationships and attributes ([terminological knowledge](https://en.wikipedia.org/wiki/Tbox)).\n", + "\n", + "In a [previous tutorial](./cuds-api.html), we have discussed how to work with CUDS objects, which represent ontology individuals. In this tutorial, we present the API of all the other entities instead: ontology classes, relationships and attributes. These are defined in an ontology installation file in [YAML](../yaml.md) or [OWL](../owl.md) format. The presented API enables you to access the entities and navigate within an ontology." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we will work both with the `city` namespace, the example namespace from OSP-core, and the `math` namespace from the [European Materials & Modelling Ontology (EMMO) ontology](https://github.com/emmo-repo/EMMO), for which an installation file is also provided with OSP-core.\n", + "\n", + "Please install the ontologies running the commands below if you have not installed them yet." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO 2021-03-31 16:16:53,174 [osp.core.ontology.installation]: Will install the following namespaces: ['city']\n", + "INFO 2021-03-31 16:16:53,187 [osp.core.ontology.yml.yml_parser]: Parsing YAML ontology file /home/jose/.local/lib/python3.9/site-packages/osp/core/ontology/docs/city.ontology.yml\n", + "INFO 2021-03-31 16:16:53,209 [osp.core.ontology.yml.yml_parser]: You can now use `from osp.core.namespaces import city`.\n", + "INFO 2021-03-31 16:16:53,209 [osp.core.ontology.parser]: Loaded 202 ontology triples in total\n", + "INFO 2021-03-31 16:16:53,223 [osp.core.ontology.installation]: Installation successful\n", + "INFO 2021-03-31 16:16:53,753 [osp.core.ontology.installation]: Will install the following namespaces: ['emmo']\n", + "INFO 2021-03-31 16:16:53,756 [osp.core.ontology.parser]: Parsing /home/jose/.local/lib/python3.9/site-packages/osp/core/ontology/docs/emmo.yml\n", + "INFO 2021-03-31 16:16:53,756 [osp.core.ontology.parser]: Downloading https://raw.githubusercontent.com/emmo-repo/emmo-repo.github.io/master/versions/1.0.0-alpha2/emmo-inferred.owl\n", + "INFO 2021-03-31 16:16:54,299 [osp.core.ontology.parser]: Parsing /tmp/tmpvqey417g-emmo\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import mereotopology`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import physical`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import top`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import semiotics`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import perceptual`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import reductionistic`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import holistic`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import physicalistic`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import math`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import properties`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import materials`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import metrology`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import models`.\n", + "INFO 2021-03-31 16:16:54,897 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import manufacturing`.\n", + "INFO 2021-03-31 16:16:54,898 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import isq`.\n", + "INFO 2021-03-31 16:16:54,898 [osp.core.ontology.parser]: You can now use `from osp.core.namespaces import siunits`.\n", + "INFO 2021-03-31 16:16:54,901 [osp.core.ontology.parser]: Loaded 4664 ontology triples in total\n", + "INFO 2021-03-31 16:16:55,083 [osp.core.ontology.installation]: Installation successful\n" + ] + } + ], + "source": [ + "# Install the ontologies\n", + "!pico install city\n", + "!pico install emmo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing entities: the namespace object\n", + "\n", + "To acces ontology entitites, we first need to know the aliases of the installed ontology namespaces. In each ontology [YAML installation file](../yaml.md), the namespace(s) that it contains is(are) stated at the top of the file. For example, at the top of the [city ontology installation file](https://github.com/simphony/osp-core/blob/master/osp/core/ontology/docs/city.ontology.yml) you may find:\n", + "\n", + "```yaml\n", + "---\n", + "version: \"0.0.3\"\n", + "\n", + "namespace: \"city\"\n", + "\n", + "ontology:\n", + " ...\n", + "```\n", + "\n", + "Alternatively, you can use [pico ontology installation tool](https://simphony.readthedocs.io/en/latest/utils.html#pico-installs-cuds-ontologies) to see the installed namespaces:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Packages:\n", + "\t- city\n", + "\t- emmo\n", + "Namespaces:\n", + "\t- xml\n", + "\t- rdf\n", + "\t- rdfs\n", + "\t- xsd\n", + "\t- cuba\n", + "\t- isq\n", + "\t- ns1\n", + "\t- ns2\n", + "\t- owl\n", + "\t- city\n", + "\t- mereotopology\n", + "\t- physical\n", + "\t- top\n", + "\t- semiotics\n", + "\t- perceptual\n", + "\t- reductionistic\n", + "\t- holistic\n", + "\t- physicalistic\n", + "\t- math\n", + "\t- properties\n", + "\t- materials\n", + "\t- metrology\n", + "\t- models\n", + "\t- manufacturing\n", + "\t- siunits\n" + ] + } + ], + "source": [ + "!pico list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once we know the name of the namespace that we want to use, we import it in python. For this tutorial, we are importing the namespaces `city` and `math`. Through those imported namespace python objects, the entities within the namespaces can be accessed:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from osp.core.namespaces import city\n", + "from osp.core.namespaces import math" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are several ways to access an ontology entity in OSP-core, which are summarized by the following list and will be demonstrated shortly after.\n", + "\n", + "- By **suffix**. For example, for the namespace `city`, whose [IRI](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/) is `http://www.osp-core.com/city#`, fetching by the suffix `Citizen` would return the ontology entity with IRI `http://www.osp-core.com/city#Citizen`.\n", + "\n", + "- By **label**. Fetchs the entity by the label that has been assigned to it using either the `rdfs:label` or `skos:prefLabel` predicates.\n", + "\n", + "- By **IRI**. The full [IRI](https://fusion.cs.uni-jena.de/fusion/blog/2016/11/18/iri-uri-url-urn-and-their-differences/) of an ontology entity is provided in order to fetch it.\n", + "\n", + "- By **string**. Using a string, for example `\"city.LivingBeing\"`. This is only useful in some special cases." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The **most convenient way** to access an ontology entity is using the **dot notation** in python. For example, `city.Citizen`. This method is a shorthand for fetching by suffix or label: \n", + "\n", + "- When the keyword `reference_by_label` is set to `True` (enabled) in the [ontology YAML installation file](../owl.md), the dot notation is a shorthand for fetching by label. This keyword is **enabled** in the `math` namespace.\n", + "\n", + "- When the keyword `reference_by_label` is set to `False` (disabled) or not set, the dot notation is a shorthand for fetching by suffix instead. This keyword is **disabled** in the `city` namespace." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Accessing an ontology entity by suffix**\n", + "\n", + "Let's fetch the Citizen class, whose IRI is `http://www.osp-core.com/city#Citizen`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The keyword, `reference_by_label` is set to `False`, so one can just use the dot notation. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "city.Citizen" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another alternative is using the `get_from_suffix` method from the namespace object. This is useful when the suffix contains characters that Python does not accept as property names, such as spaces or dashes." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "city.get_from_suffix('Citizen')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the suffix is case sensitive, and therefore the following would produce an error." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "#city.citizen # -> Fails." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Accessing an ontology entity by label**\n", + "\n", + "Let's fetch the Integer class, whose IRI is `http://emmo.info/emmo/middle/math#EMMO_f8bd64d5_5d3e_4ad4_a46e_c30714fecb7f`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The keyword `reference_by_label` is set to `True`, so we just use the dot notation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.Integer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another alternative is using the square bracket notation on the namespace object. This is useful when the suffix contains characters that Python does not accept as property names, such as spaces or dashes." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math['Integer']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fetching by label is NOT case sensitive when using the dot notation, but it is when using square brackets, so the following behavior is expected." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#math['integer'] # -> Fails.\n", + "math.integer # -> Works." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Accessing an ontology entity by IRI**\n", + "\n", + "This is only possible using the `get_from_iri` method from the namespace object. For example, let's fetch the Integer entity again." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.get_from_iri('http://emmo.info/emmo/middle/math#EMMO_f8bd64d5_5d3e_4ad4_a46e_c30714fecb7f')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Access entities using a string**\n", + "\n", + "Sometimes you only have a string refering to an entity. Using the `get_entity` function you can get the corresponding python object easily:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can get an entity with a string\n", + "city.LivingBeing\n", + "True\n" + ] + } + ], + "source": [ + "from osp.core.namespaces import get_entity # noqa: E402\n", + "\n", + "print(\"\\nYou can get an entity with a string\")\n", + "print(get_entity(\"city.LivingBeing\"))\n", + "print(get_entity(\"city.LivingBeing\") == city.LivingBeing)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing an entity's name, IRI and namespace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each ontology entity has an associated name which can be accessed using the `name` property. " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'LivingBeing'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "city.LivingBeing.name" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The IRI of an entity might be accessed using the `iri` property." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "rdflib.term.URIRef('http://emmo.info/emmo/middle/math#EMMO_18d180e4_5e3e_42f7_820c_e08951223486')" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.Real.iri" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition, it is possible to get the namespace object to which the entity belongs using the `namespace` property." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.Equation.namespace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing super- and subclasses\n", + "\n", + "Using the properties `superclasses` and `subclasses` it is easy to navigate the ontology. Direct superclasses and subclasses can also be accessed:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can access the superclasses and the subclasses\n", + "{, }\n", + "{, , }\n", + "\n", + "You can access the direct superclasses and subclasses\n", + "{}\n", + "{}\n", + "\n", + "You can access a description of the entities\n", + "A being that lives\n", + "\n", + "You can test if one entity is a subclass / superclass of another\n", + "True\n", + "True\n" + ] + } + ], + "source": [ + "print(\"\\nYou can access the superclasses and the subclasses\")\n", + "print(city.LivingBeing.superclasses)\n", + "print(city.LivingBeing.subclasses)\n", + "\n", + "print(\"\\nYou can access the direct superclasses and subclasses\")\n", + "print(city.LivingBeing.direct_superclasses)\n", + "print(city.LivingBeing.direct_subclasses)\n", + "\n", + "print(\"\\nYou can access a description of the entities\")\n", + "print(city.LivingBeing.description)\n", + "\n", + "print(\"\\nYou can test if one entity is a subclass / superclass of another\")\n", + "print(city.Person.is_subclass_of(city.LivingBeing))\n", + "print(city.LivingBeing.is_superclass_of(city.Person))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Testing the type of the entities\n", + "\n", + "In the ontology, three types of entities can be defined: classes, relationships and attributes. OSP-core has its own vocabulary, the [CUBA namespace](../yaml.html#the-cuba-namespace), which describes, among other things, such entity types. Relationships are subclasses of `CUBA.RELATIONSHIP` and attributes are subclasses of `CUBA.ATTRIBUTE`. There are different Python objects for the different entity types. You can use both to check which type of entity you are dealing with:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can test if an entity is a class\n", + "True\n", + "True\n", + "\n", + "You can test if an entity is a relationship\n", + "True\n", + "True\n", + "\n", + "You can test if an entity is a attribute\n", + "True\n", + "True\n" + ] + } + ], + "source": [ + "from osp.core.namespaces import cuba # noqa: E402\n", + "\n", + "# These are the classes for the ontology entities\n", + "from osp.core.ontology import ( # noqa: F401, E402\n", + " OntologyEntity,\n", + " OntologyClass,\n", + " OntologyRelationship,\n", + " OntologyAttribute\n", + ")\n", + "\n", + "print(\"\\nYou can test if an entity is a class\")\n", + "print(isinstance(city.LivingBeing, OntologyClass))\n", + "print(not city.LivingBeing.is_subclass_of(cuba.relationship)\n", + " and not city.LivingBeing.is_subclass_of(cuba.attribute))\n", + "\n", + "print(\"\\nYou can test if an entity is a relationship\")\n", + "print(isinstance(city.hasInhabitant, OntologyRelationship))\n", + "print(city.hasInhabitant.is_subclass_of(cuba.relationship))\n", + "\n", + "print(\"\\nYou can test if an entity is a attribute\")\n", + "print(isinstance(city.name, OntologyAttribute))\n", + "print(city.name.is_subclass_of(cuba.attribute))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations specific to ontology classes\n", + "\n", + "The different types of entities differ in the operations they offer. For classes, you can access the attributes:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can get the attributes of an ontology class and their defaults\n", + "{: (rdflib.term.Literal('John Smith'), False, None), : (rdflib.term.Literal('25', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')), False, None)}\n", + "\n", + "You can get the non-inherited attributes and their defaults\n", + "{}\n", + "{: (rdflib.term.Literal('John Smith'), False, None), : (rdflib.term.Literal('25', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')), False, None)}\n" + ] + } + ], + "source": [ + "print(\"\\nYou can get the attributes of an ontology class and their defaults\")\n", + "print(city.Citizen.attributes)\n", + "\n", + "print(\"\\nYou can get the non-inherited attributes and their defaults\")\n", + "print(city.Citizen.own_attributes)\n", + "print(city.LivingBeing.own_attributes)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition, OSP-core has special support for the `owl:Restriction` and `owl:Composition` classes of the [Web Ontology Language (OWL)](https://en.wikipedia.org/wiki/Web_Ontology_Language) (check the [OWL ontology specification](https://www.w3.org/TR/owl2-syntax/) for more details). Such OWL classes are represented by the python classes `Restriction` and `Composition`. See [operations specific to ontology axioms](./ontology-interface.html#Operations-specific-to-ontology-axioms) for more information.\n", + "\n", + "For example, in the city ontology, the citizens have a restriction on the name and age attributes: a citizen must have exactly one name and one age. These axioms can be accessed using the `axioms` property, which returns both the restriction and compositions affecting the class." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('city.name QUANTIFIER.EXACTLY 1', 'city.age QUANTIFIER.EXACTLY 1')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple(str(x) for x in city.Citizen.axioms)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations specific to ontology axioms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For restrictions, the quantifier, the target, the restriction type and the relationship/attribute (depending on whether it is a restriction of the relationship type or attribute type) may be accessed." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "city.name QUANTIFIER.EXACTLY 1\n", + "QUANTIFIER.EXACTLY\n", + "1\n", + "RTYPE.ATTRIBUTE_RESTRICTION\n", + "city.name\n" + ] + } + ], + "source": [ + "restriction = city.Citizen.axioms[0]\n", + "print(restriction)\n", + "print(restriction.quantifier)\n", + "print(restriction.target)\n", + "print(restriction.rtype)\n", + "print(restriction.attribute)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For compositions, both the operator and operands can be accesed." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(math.Mathematical OPERATOR.AND perceptual.Symbol)\n", + "OPERATOR.AND\n", + "[, ]\n" + ] + } + ], + "source": [ + "from osp.core.ontology.oclass_composition import Composition\n", + "composition = tuple(x for x in math.Integer.axioms if type(x) is Composition)[0]\n", + "print(composition)\n", + "print(composition.operator)\n", + "print(composition.operands)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations specific to ontology relationships\n", + "\n", + "You can access the inverse of relationships." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can get the inverse of a relationship\n", + "city.INVERSE_OF_hasInhabitant\n" + ] + } + ], + "source": [ + "print(\"\\nYou can get the inverse of a relationship\")\n", + "print(city.hasInhabitant.inverse)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations specific to attributes\n", + "\n", + "You can acces the datatype and the argument name of attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can get the argument name of an attribute. The argument name is used as keyword argument when instantiating CUDS objects.\n", + "age\n", + "\n", + "You can get the datatype of attributes\n", + "http://www.w3.org/2001/XMLSchema#integer\n", + "\n", + "You can use the attribute to convert values to the datatype of the attribute\n", + " 10\n", + "\n", + "And likewise to convert values to the python basic type associated with the datatype of the attribute.\n", + " 5\n" + ] + } + ], + "source": [ + "print(\"\\nYou can get the argument name of an attribute. \"\n", + " \"The argument name is used as keyword argument when instantiating CUDS objects.\")\n", + "print(city.age.argname)\n", + "\n", + "print(\"\\nYou can get the datatype of attributes\")\n", + "print(city.age.datatype)\n", + "\n", + "print(\"\\nYou can use the attribute to convert values \"\n", + " \"to the datatype of the attribute\")\n", + "result = city.age.convert_to_datatype(\"10\")\n", + "print(type(result), result)\n", + "\n", + "print(\"\\nAnd likewise to convert values to the python basic type \"\n", + " \"associated with the datatype of the attribute.\")\n", + "result = city.name.convert_to_basic_type(5)\n", + "print(type(result), result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Check the API Reference for more details on the methods [_convert_to_datatype_](http://127.0.0.1:8000/api_ref.html#osp.core.ontology.attribute.OntologyAttribute.convert_to_datatype) and [_convert_to_basic_type_](http://127.0.0.1:8000/api_ref.html#osp.core.ontology.attribute.OntologyAttribute.convert_to_basic_type)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating CUDS using ontology classes\n", + "\n", + "You can call ontology classes to create CUDS objects. To learn more, have a look at the [CUDS API tutorial](./cuds-api.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "You can instantiate CUDS objects using ontology classes\n", + "city.Citizen: e0947100-9c40-415f-92c8-a86b796dbb01\n", + "\n", + "You can check if a CUDS object is an instance of a ontology class\n", + "True\n", + "True\n", + "\n", + "You can get the ontology class of a CUDS object.\n", + "city.Citizen\n" + ] + } + ], + "source": [ + "print(\"\\nYou can instantiate CUDS objects using ontology classes\")\n", + "print(city.Citizen(name=\"Test Person\", age=42))\n", + "\n", + "print(\"\\nYou can check if a CUDS object is an instance of a ontology class\")\n", + "print(city.Citizen(name=\"Test Person\", age=42).is_a(city.Citizen))\n", + "print(city.Citizen(name=\"Test Person\", age=42).is_a(city.LivingBeing))\n", + "\n", + "print(\"\\nYou can get the ontology class of a CUDS object.\")\n", + "print(city.Citizen(name=\"Test Person\", age=42).oclass)\n" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/jupyter/quantum_espresso.ipynb b/docs/source/jupyter/quantum_espresso.ipynb new file mode 100644 index 0000000..52b3269 --- /dev/null +++ b/docs/source/jupyter/quantum_espresso.ipynb @@ -0,0 +1,447 @@ +{ + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "orig_nbformat": 2, + "kernelspec": { + "name": "python_defaultSpec_1597048081738", + "display_name": "Python 3.8.2 64-bit" + } + }, + "nbformat": 4, + "nbformat_minor": 2, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial: Quantum ESPRESSO wrapper\n", + "\n", + "In this tutorial we will go through a simple example of how to use the wrapper for the Quantum Espresso simulation engine.\n", + "You can find the wrapper [here](https://github.com/simphony/quantum-espresso-wrapper).\n", + "\n", + "## Background\n", + "This is an example of a slightly different design based upon the input-output functionality of certain simulation engines such as Quantum Espresso and Gromacs.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Let's get hands-on\n", + "### Installation\n", + "To run the local installation of Quantum Espresso, simply run `./install_engine.sh`. This should check for the prerequisites and compile the code for Quantum Espresso for you.\n", + "\n", + "If the script runs into an error finding openmpi-bin or something like that, try running `apt-get update` and try again. \n", + "Once the installation has completed, try running `pw.x` to see if the installation has succeeded. If this does not work, then try adding `export PATH=$PATH:/home/username/qe-6.1/bin/` at the end of `.bashrc` located at your home folder. \n", + " \n", + "Once you have verified that `pw.x` works, install the ontology via `pico install ontology.simlammps.yml`, and make sure to run `python3 setup.py` located in the root of the quantum espresso wrapper folder. \n", + "\n", + "That should be all needed to use Quantum Espresso!\n", + "\n", + "### Simple example\n", + "\n", + "This is an adaptation of quantum-espresso-wrapper/examples/Simple.py. As usual, we need to import the necessary components:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np \n", + "\n", + "from osp.core.namespaces import QE\n", + "from osp.core.utils import pretty_print\n", + "from osp.wrappers.quantumespresso.qe_session import qeSession" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we create simulation and its K points, which determine at what points it samples the cell" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "sim = QE.Simulation()\n", + "k = QE.K_POINTS(vector = (7, 7, 7), unit = \"\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we create a cell, the element Silicon, a pseudopotential, an atom and the cell parameters. Note that the pseudopotential files should ALWAYS be located inside of a folder named `$PSEUDO_DIR` inside of wherever you are running the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "SiCell = QE.Cell()\n", + "Si = QE.Element(name = \"Si\")\n", + "SiPseudo = QE.PSEUDOPOTENTIAL(name = \"Si.pbe-n-kjpaw_psl.1.0.0.UPF\")\n", + "Si1 = QE.Atom()\n", + "SiParams = QE.CellParams()\n", + "celldm1 = QE.Celldm1(value = 5.43070, unit = \"au\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we connect these all to each other using the `add` method." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "" + }, + "metadata": {}, + "execution_count": 4 + } + ], + "source": [ + "Si.add(SiPseudo, Si1)\n", + "Si.add(QE.Mass(value = 28.085, unit = \"amu\"))\n", + "SiCell.add(Si1, SiParams)\n", + "Si1.add(QE.Position(vector = (0, 0, 0), unit = \"\"))\n", + "SiCell.add(celldm1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We specify the cell parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "[,\n ,\n ]" + }, + "metadata": {}, + "execution_count": 5 + } + ], + "source": [ + "SiParams.add(QE.CellParameterX(vector = (0.5, 0.5, 0), unit = \"\"),\n", + " QE.CellParameterY(vector = (0.5, 0, 0.5), unit = \"\"),\n", + " QE.CellParameterZ(vector = (0, 0.5, 0.5), unit = \"\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And then we add everything created so far to the simulation:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "" + }, + "metadata": {}, + "execution_count": 6 + } + ], + "source": [ + "sim.add(SiCell)\n", + "sim.add(Si)\n", + "sim.add(k)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While we're add it, let's add some variables to the simulation which we can check to see if they have been updated. They will not be taken into account when simulating, so they're there for control purposes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "" + }, + "metadata": {}, + "execution_count": 7 + } + ], + "source": [ + "sim.add(QE.Pressure(value = 100, unit = \"kbar\"))\n", + "sim.add(QE.StressTensor(tensor2 = np.zeros((3, 3)), unit = \"kbar\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check out what this simulation looks like now with the `pretty_print` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "- Cuds object:\n uuid: 903145ad-50e3-46fc-9d28-1aa1ec364e8a\n type: qe.Simulation\n superclasses: cuba.Class, cuba.Entity, qe.Simulation\n description: \n All components of the simulation that are needed to run the model\n\n |_Relationship qe.HAS_PART:\n - qe.Cell cuds object:\n . uuid: 67a0fcb4-4977-49df-9bcb-13bca17b2763\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 1bad1c25-609a-4bc0-8c65-3c0167cfdbe2\n . . |_Relationship qe.HAS_PART:\n . . - qe.Position cuds object:\n . . uuid: c70afbb3-0012-488d-8059-b44d775c6b23\n . . vector: [0. 0. 0.]\n . . unit: \n . - qe.CellParams cuds object:\n . . uuid: f444899a-e850-4ab2-b79e-c91026523eb3\n . . |_Relationship qe.HAS_PART:\n . . - qe.CellParameterX cuds object:\n . . . uuid: 0ebdeed9-1d8a-498d-94c9-bafccb05d652\n . . . vector: [0.5 0.5 0. ]\n . . . unit: \n . . - qe.CellParameterY cuds object:\n . . . uuid: fea8789c-8c07-49f9-9971-8d42bdd6ba3f\n . . . vector: [0.5 0. 0.5]\n . . . unit: \n . . - qe.CellParameterZ cuds object:\n . . uuid: 1474d106-4204-428d-827b-2d5e2cb4af51\n . . vector: [0. 0.5 0.5]\n . . unit: \n . - qe.Celldm1 cuds object:\n . uuid: be8f3915-3eb7-4221-a441-345eda51832b\n . unit: au\n . value: 5.4307\n - qe.Element cuds object named :\n . uuid: 8628ceb7-1c02-4014-95a4-d9450aab4753\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 1bad1c25-609a-4bc0-8c65-3c0167cfdbe2\n . . (already printed)\n . - qe.Mass cuds object:\n . . uuid: 1aee515a-4e12-40e6-bbd6-23bf5c95fe84\n . . unit: amu\n . . value: 28.085\n . - qe.PSEUDOPOTENTIAL cuds object named :\n . uuid: cb27bcb9-27c6-48a9-8f1e-8977b16567c5\n - qe.K_POINTS cuds object:\n . uuid: 6847a5f0-8d20-4f73-9eb3-043e78053182\n . vector: [7. 7. 7.]\n . unit: \n - qe.Pressure cuds object:\n . uuid: d281d93d-fd10-41fd-868c-0cda3b510431\n . unit: kbar\n . value: 100.0\n - qe.StressTensor cuds object:\n uuid: 2f302f8b-89b8-4d7a-a2a7-4f6e19737f00\n unit: kbar\n tensor2: [[0. 0. 0.]\n [0. 0. 0.]\n [0. 0. 0.]]\n" + } + ], + "source": [ + "pretty_print(sim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, it's time to get the simulation running:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Running calculation...\n/mnt/c/iwm/docs/si.pwscf.in\npw.x -i /mnt/c/iwm/docs/si.pwscf.in > /mnt/c/iwm/docs/si.pwscf.out\n" + } + ], + "source": [ + "session = qeSession()\n", + "quantum_espresso_wrapper = QE.QEWrapper(session = session)\n", + "quantum_espresso_wrapper.add(sim)\n", + "print(\"Running calculation...\")\n", + "\n", + "quantum_espresso_wrapper.session._run(simulation = sim, prefix = \"si\", command_type = \"pw.x\", calculation_type = \"scf\", root = \"\", CONTROL = {'pseudo_dir': \"'.'\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's check the results of our calculation:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "- Cuds object:\n uuid: 903145ad-50e3-46fc-9d28-1aa1ec364e8a\n type: qe.Simulation\n superclasses: cuba.Class, cuba.Entity, qe.Simulation\n description: \n All components of the simulation that are needed to run the model\n\n |_Relationship qe.HAS_PART:\n - qe.Cell cuds object:\n . uuid: 67a0fcb4-4977-49df-9bcb-13bca17b2763\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 1bad1c25-609a-4bc0-8c65-3c0167cfdbe2\n . . |_Relationship qe.HAS_PART:\n . . - qe.Position cuds object:\n . . uuid: c70afbb3-0012-488d-8059-b44d775c6b23\n . . vector: [0. 0. 0.]\n . . unit: \n . - qe.CellParams cuds object:\n . . uuid: f444899a-e850-4ab2-b79e-c91026523eb3\n . . |_Relationship qe.HAS_PART:\n . . - qe.CellParameterX cuds object:\n . . . uuid: 0ebdeed9-1d8a-498d-94c9-bafccb05d652\n . . . vector: [0.5 0.5 0. ]\n . . . unit: \n . . - qe.CellParameterY cuds object:\n . . . uuid: fea8789c-8c07-49f9-9971-8d42bdd6ba3f\n . . . vector: [0.5 0. 0.5]\n . . . unit: \n . . - qe.CellParameterZ cuds object:\n . . uuid: 1474d106-4204-428d-827b-2d5e2cb4af51\n . . vector: [0. 0.5 0.5]\n . . unit: \n . - qe.Celldm1 cuds object:\n . uuid: be8f3915-3eb7-4221-a441-345eda51832b\n . unit: au\n . value: 5.4307\n - qe.Element cuds object named :\n . uuid: 8628ceb7-1c02-4014-95a4-d9450aab4753\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 1bad1c25-609a-4bc0-8c65-3c0167cfdbe2\n . . (already printed)\n . - qe.Mass cuds object:\n . . uuid: 1aee515a-4e12-40e6-bbd6-23bf5c95fe84\n . . unit: amu\n . . value: 28.085\n . - qe.PSEUDOPOTENTIAL cuds object named :\n . uuid: cb27bcb9-27c6-48a9-8f1e-8977b16567c5\n - qe.K_POINTS cuds object:\n . uuid: 6847a5f0-8d20-4f73-9eb3-043e78053182\n . vector: [7. 7. 7.]\n . unit: \n - qe.Pressure cuds object:\n . uuid: d281d93d-fd10-41fd-868c-0cda3b510431\n . unit: kbar\n . value: 100.0\n - qe.PwOut cuds object:\n . uuid: 15c6637e-9124-44dd-a1d3-f225203c1bfc\n . path: /mnt/c/iwm/docs/si.pwscf.out\n - qe.StressTensor cuds object:\n uuid: 2f302f8b-89b8-4d7a-a2a7-4f6e19737f00\n unit: kbar\n tensor2: [[0. 0. 0.]\n [0. 0. 0.]\n [0. 0. 0.]]\n" + } + ], + "source": [ + "pretty_print(sim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the original part of the cuds tree is still there, with everything mostly the same. The new parts are:\n", + "\n", + "- The qe.PwOut cuds object. This is the output file of the simulation, in case there is something that the wrapper does not parse but that you would still like to see.\n", + "- The qe.TotalEnergy cuds object. This was parsed from the qe.PwOut file itself.\n", + "- The qe.Force cuds object. This represents the force exerted on the atom(s).\n", + "\n", + "The updated parts are:\n", + "\n", + "- The qe.Pressure cuds object, having changed in value from 100 kbar to 5723.64 kbar.\n", + "- The qe.StressTensor cuds object, which is no longer zero." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see if we can do better and calculate some bands structures:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "output_type": "error", + "ename": "TypeError", + "evalue": "_run() missing 1 required positional argument: 'simulation'", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mquantum_espresso_wrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprefix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"si\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"pw.x\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcalculation_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"bands\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mquantum_espresso_wrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprefix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"si\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"bands.x\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcalculation_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: _run() missing 1 required positional argument: 'simulation'" + ] + } + ], + "source": [ + "quantum_espresso_wrapper.session._run(prefix = \"si\", command_type = \"pw.x\", calculation_type = \"bands\")\n", + "quantum_espresso_wrapper.session._run(prefix = \"si\", command_type = \"bands.x\", calculation_type = \"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although the cuds structure won't have changed much by this, the data is there in the folder.\n", + "\n", + "Now let's try to relax this cell. While it isn't a real cell, we can still perform the calculations to relax it to know what the movement of the atoms would be like if it were a real cell (warning, perform vc-relax type calculations with caution. These examples are designed to be lightweight and non-indicative of real-world applications)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "output_type": "error", + "ename": "TypeError", + "evalue": "_run() missing 1 required positional argument: 'simulation'", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mquantum_espresso_wrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprefix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"si\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"pw.x\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcalculation_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"relax\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mIONS\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m'ion_dynamics'\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m\"'bfgs'\"\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: _run() missing 1 required positional argument: 'simulation'" + ] + } + ], + "source": [ + "quantum_espresso_wrapper.session._run(simulation = sim, prefix = \"si\", command_type = \"pw.x\", calculation_type = \"relax\", IONS = {'ion_dynamics': \"'bfgs'\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "- Cuds object:\n uuid: 90361daa-6905-4566-979e-11b3b0dd4e85\n type: qe.Simulation\n superclasses: cuba.Class, cuba.Entity, qe.Simulation\n description: \n All components of the simulation that are needed to run the model\n\n |_Relationship qe.HAS_PART:\n - qe.Cell cuds object:\n . uuid: f7548873-28e9-4d76-86da-6fddb687d29e\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 53114a1f-ebbb-4e4b-a115-080925d9eaa8\n . . |_Relationship qe.HAS_PART:\n . . - qe.Position cuds object:\n . . uuid: 48d4483b-7c72-4454-8041-581dc73fd216\n . . vector: [0. 0. 0.]\n . . unit: \n . - qe.CellParams cuds object:\n . . uuid: 9d61e990-2509-474b-935e-618ca11bb40d\n . . |_Relationship qe.HAS_PART:\n . . - qe.CellParameterX cuds object:\n . . . uuid: f2655054-efa7-4b39-9f0a-cf6453be68ec\n . . . vector: [0.5 0.5 0. ]\n . . . unit: \n . . - qe.CellParameterY cuds object:\n . . . uuid: 55647575-ea8f-4ef7-aee8-2a3333a4ec71\n . . . vector: [0.5 0. 0.5]\n . . . unit: \n . . - qe.CellParameterZ cuds object:\n . . uuid: 92be9c63-ee80-46d9-8853-ccb562e94a5b\n . . vector: [0. 0.5 0.5]\n . . unit: \n . - qe.Celldm1 cuds object:\n . . uuid: db776c65-9d2e-448e-bc55-5fe0f9c7ee75\n . . unit: au\n . . value: 5.4307\n . - qe.Element cuds object named :\n . uuid: 14dacecb-023c-4ace-9e83-35b0ecaa1032\n . |_Relationship qe.HAS_PART:\n . - qe.Atom cuds object:\n . . uuid: 53114a1f-ebbb-4e4b-a115-080925d9eaa8\n . . (already printed)\n . - qe.Atom cuds object:\n . . uuid: c2094a19-8769-4298-a50a-be1f8befe5bf\n . . |_Relationship qe.HAS_PART:\n . . - qe.Position cuds object:\n . . uuid: 4087ce47-16f0-4449-b8c4-4577e6d265e2\n . . vector: [0.25 0.25 0.26]\n . . unit: \n . - qe.Mass cuds object:\n . . uuid: 5d57a768-d315-4f91-84a8-fcfad9aae382\n . . unit: amu\n . . value: 28.085\n . - qe.PSEUDOPOTENTIAL cuds object named :\n . uuid: ab064983-5cc9-418e-a9e7-3357c04388f5\n - qe.Element cuds object named :\n . uuid: 14dacecb-023c-4ace-9e83-35b0ecaa1032\n . (already printed)\n - qe.K_POINTS cuds object:\n . uuid: 38385f3b-128c-491f-91f4-44de15055d56\n . vector: [7. 7. 7.]\n . unit: \n - qe.Pressure cuds object:\n . uuid: 57f47fa8-4588-488f-8c89-fa8a0f37f567\n . unit: kbar\n . value: 100.0\n - qe.Pressure cuds object:\n . uuid: beac6977-01cf-4b4d-b89b-1b4d4eecf5c0\n . unit: kbar\n . value: 100.0\n - qe.PwOut cuds object:\n . uuid: 7890c86c-9f6f-42b3-a02c-cd57f5307c75\n . path: si.pwscf.out\n - qe.PwOut cuds object:\n . uuid: 5c7e31f9-6e55-4ddf-af62-5a3e58811464\n . path: si.pwscf.out\n - qe.StressTensor cuds object:\n . uuid: 16b33d51-0f6d-4451-a919-da494a13082f\n . unit: kbar\n . tensor2: [[0. 0. 0.]\n [0. 0. 0.]\n [0. 0. 0.]]\n - qe.StressTensor cuds object:\n uuid: 590ad64e-d110-4711-90c6-0986bb53dafc\n unit: kbar\n tensor2: [[0. 0. 0.]\n [0. 0. 0.]\n [0. 0. 0.]]\n" + } + ], + "source": [ + "pretty_print(sim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, the position hasn't changed. So let's spice things up a little bit and add another atom, and then relax:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "output_type": "error", + "ename": "ValueError", + "evalue": " is already in the container", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mSi2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mQE\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mAtom\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mSi2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mQE\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPosition\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvector\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m0.25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.26\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mSiCell\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0mSi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSi2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mpretty_print\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msim\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/c/IWM/osp-core-3.4.0-dev/osp/core/cuds.py\u001b[0m in \u001b[0;36madd\u001b[0;34m(self, rel, *args)\u001b[0m\n\u001b[1;32m 161\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrel\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_neighbors\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0marg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0muid\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_neighbors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mrel\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 162\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'{!r} is already in the container'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 163\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 164\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0marg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[0marg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recursive_store\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mold_objects\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: is already in the container" + ] + } + ], + "source": [ + "Si2 = QE.Atom()\n", + "Si2.add(QE.Position(vector = (0.25, 0.25, 0.26), unit = \"\"))\n", + "SiCell.add(Si)\n", + "Si.add(Si2)\n", + "pretty_print(sim)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "output_type": "error", + "ename": "TypeError", + "evalue": "_run() missing 1 required positional argument: 'simulation'", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mquantum_espresso_wrapper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprefix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"si\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"pw.x\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcalculation_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"relax\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mIONS\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m'ion_dynamics'\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m\"'bfgs'\"\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mpretty_print\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msim\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: _run() missing 1 required positional argument: 'simulation'" + ] + } + ], + "source": [ + "quantum_espresso_wrapper.session._run(simulation = sim, prefix = \"si\", command_type = \"pw.x\", calculation_type = \"relax\", IONS = {'ion_dynamics': \"'bfgs'\"})\n", + "pretty_print(sim)" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/docs/source/jupyter/sessions_and_vars.ipynb b/docs/source/jupyter/sessions_and_vars.ipynb index f7b42eb..ce4ee97 100644 --- a/docs/source/jupyter/sessions_and_vars.ipynb +++ b/docs/source/jupyter/sessions_and_vars.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Tutorial: Sessions and variables\n", - "In this tutorial we will explain how objects and the variables assigned to them behave with different sessions.\n", + "In this tutorial we will explain how objects and the variables assigned to them behave with different sessions. This tutorial is not available on binder since `wrapper_x` and `wrapper_y` are fictional implementations for educational purposes.\n", "\n", "## Background\n", "Some use cases of OSP-core, like coupling and linking, require interaction between multiple sessions. Even using just one wrapper usually means working with the default CoreSession initially, and then the wrapper session.\n", diff --git a/docs/source/jupyter/simlammps.ipynb b/docs/source/jupyter/simlammps.ipynb index 27b20af..70441b8 100644 --- a/docs/source/jupyter/simlammps.ipynb +++ b/docs/source/jupyter/simlammps.ipynb @@ -27,7 +27,7 @@ "- Using Docker: run `./docker_install.sh`.\n", "- Local installation: remember that you must have a compatible version of [OSP-core installed](../installation.md#osp-core-installation).\n", " \n", - " Install the ontology via `pico install ontology.simlammps.yml.`\n", + " Install the ontology via `pico install simlammps.ontology.yml.`\n", " \n", " Run `./install_engine.sh`.\n", "\n", @@ -77,8 +77,7 @@ ], "source": [ "simlammps_session = SimlammpsSession()\n", - "simlammps = simlammps_ontology.SimlammpsWrapper(session=simlammps_session)\n", - "" + "simlammps = simlammps_ontology.SimlammpsWrapper(session=simlammps_session)\n" ] }, { @@ -297,27 +296,7 @@ "source": [] } ], - "metadata": { - "language_info": { - "name": "python", - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "version": "3.7.5-final" - }, - "orig_nbformat": 2, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "npconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": 3, - "kernelspec": { - "name": "python37564bitvenvvenvbd0fd6573f5f4e40af04d1a32ae4ddbe", - "display_name": "Python 3.7.5 64-bit ('venv': venv)" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 2 } \ No newline at end of file diff --git a/docs/source/jupyter/wrapper_development.ipynb b/docs/source/jupyter/wrapper_development.ipynb index 53460c3..e9fc51a 100644 --- a/docs/source/jupyter/wrapper_development.ipynb +++ b/docs/source/jupyter/wrapper_development.ipynb @@ -4,7 +4,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Tutorial: Simple Wrapper development\n", + "# Tutorial: Simple wrapper development\n", + "\n", + "
\n", + "\n", + "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/simphony/docs/master?filepath=docs%2Fsource%2Fjupyter%2Fwrapper_development.ipynb \"Click to run the tutorial yourself!\")\n", + " \n", + "
\n", + "\n", + "\n", "In this tutorial we will implement a very simple simulation wrapper.\n", "It can be used to understand which methods need to be implemented, and how.\n", "\n", @@ -19,6 +27,17 @@ "Remember that once you have OSP-core installed and the ontology file locally, you can simply run `pico install `" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You can download and install the ontology by running this cell\n", + "!curl -s https://raw.githubusercontent.com/simphony/wrapper-development/master/osp/wrappers/simple_simulation/simple_ontology.ontology.yml -o simple_ontology.ontology.yml\n", + "!pico install simple_ontology.ontology.yml" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -116,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -391,27 +410,7 @@ "source": [] } ], - "metadata": { - "language_info": { - "name": "python", - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "version": "3.7.5-final" - }, - "orig_nbformat": 2, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "npconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": 3, - "kernelspec": { - "name": "python37564bitvenvvenvbd0fd6573f5f4e40af04d1a32ae4ddbe", - "display_name": "Python 3.7.5 64-bit ('venv': venv)" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 2 } \ No newline at end of file diff --git a/docs/source/license.md b/docs/source/license.md new file mode 100644 index 0000000..2996731 --- /dev/null +++ b/docs/source/license.md @@ -0,0 +1,13 @@ +# License +BSD 3-Clause +Copyright 2020 SimPhoNy OSP-core developers. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/source/links.md b/docs/source/links.md index 46335bf..76ac9b7 100644 --- a/docs/source/links.md +++ b/docs/source/links.md @@ -6,7 +6,7 @@ SimPhoNy: - [GitHub's SimPhoNy group](https://github.com/simphony) - [OSP-core](https://github.com/simphony/osp-core) - [wrappers](https://gitlab.cc-asp.fraunhofer.de/simphony/wrappers) -- [wrapper development](https://gitlab.cc-asp.fraunhofer.de/simphony/wrappers/wrapper-development/) +- [wrapper development](https://github.com/simphony/wrapper-development) Technologies used: - [Docker](https://www.docker.com/), used for the CI and engines @@ -17,14 +17,40 @@ Technologies used: SimPhoNy OSP-core and wrappers development is supported by the following Grants: ```eval_rst -=========== ============ =========================== =============== -Project Funded by Call Grant Agreement -=========== ============ =========================== =============== -SimPhoNy FP7 NMP-2013-1.4-1 604005 -MarketPlace Horizon 2020 H2020-NMBP-TO-IND-2016-2017 760173 -FORCE Horizon 2020 H2020-NMBP-TO-IND-2016-2017 721027 -SimDOME Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814492 -INTERSECT Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814487 -ReaxPRO Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814416 -=========== ============ =========================== =============== -``` \ No newline at end of file +============= ============ =============================== ================== +Project Programme Call ID Grant Agreement ID +============= ============ =============================== ================== +SimPhoNy FP7 NMP-2013-1.4-1 604005 +MarketPlace Horizon 2020 H2020-NMBP-TO-IND-2016-2017 760173 +FORCE Horizon 2020 H2020-NMBP-TO-IND-2016-2017 721027 +SimDOME Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814492 +OYSTER Horizon 2020 H2020-NMBP-2017-two-stage 760827 +INTERSECT Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814487 +ReaxPRO Horizon 2020 H2020-NMBP-TO-IND-2018-2020 814416 +APACHE Horizon 2020 H2020-NMBP-ST-IND-2018 814496 +NanoMECommons Horizon 2020 H2020-NMBP-TO-IND-2020-twostage 952869 +OntoTRANS Horizon 2020 H2020-NMBP-TO-IND-2019 862136 +============= ============ =============================== ================== +``` + +Some of the explanations and background provided have been adapted from Pablo de Andres' +master thesis on "Natural Language Search on an ontology-based data structure". + +# Compatibility table +The following table describes the compatibilities between of SimPhoNy docs and OSP-core. + +```eval_rst + +.. table:: + :align: center + :widths: auto + + ============= ========== + SimPhoNy docs OSP-core + ============= ========== + 2.4.x 3.5.2-beta + 2.3.x 3.4.0-beta + 2.2.x 3.3.5-beta + 2.1.x 3.3.0-beta + ============= ========== +``` diff --git a/docs/source/motivation.md b/docs/source/motivation.md deleted file mode 100644 index 2493742..0000000 --- a/docs/source/motivation.md +++ /dev/null @@ -1,143 +0,0 @@ -## Motivation -In this section, we will present some of the main concepts SimPhoNy was created to tackle. -### Operability -There is a multitude of tools and programs out there, all with their own formats and protocols. - -Every time a user wants to use one of these tools, they must familiarise themselves with the software. -Furthermore, if they want to integrate multiple tools in one workflow, the must, in most cases, -take care of the conversion on their own. - -Based on how tools communicate with other tools, we can define 3 levels of operability: - -#### Compatibility - ```eval_rst - .. uml:: - :align: center - :caption: Compatibility - - rectangle A - rectangle B - rectangle C - rectangle D - - A <-> B - A <-[hidden]- C - C <-> D - ``` - - When we say two tools are compatible, they are able to communicate with each other - in a one to one basis. - This means the tools must either use the same format, or be able to convert to the format of the other. - - If we compare this to speaking languages, you could say `A` and `B`, or `C` and `D` speak the same language. - However, `A` has no way to talk with `C` or `D`, for example. - -#### De Facto Standard - ```eval_rst - .. uml:: - :align: center - :caption: De Facto Standard - - rectangle A - rectangle B - rectangle C - rectangle D - - A <--> B - A <-> C - C <-[hidden]- D - B <-[hidden]- D - A <-> D - ``` - - In this case, the level of operability is higher. - All tools know how to communicate with a tool whose format has become a de facto standard. - - To continue with our language simile, `A` would be a translator that speaks the languages of `B`, `C` and `D`. - If `B` wants to talk to `C`, they must first relay the message to `A`, - and `A` will convert it to a format that `C` understands. - -#### Interoperability - ```eval_rst - .. uml:: - :align: center - :caption: Interoperability - - usecase x as "open standard" - rectangle A - rectangle B - rectangle C - rectangle D - A <-down-> x - B <-right-> x - C <-left-> x - D <-up-> x - ``` - The highest level of operability is interoperability. - Here there is no need for all tools to go through the De Facto standard, - because there is a format that is known by all of them and enables all components to communicate among themselves. - - This final stage could be compared to all parties learning a language like - [Esperanto](https://en.wikipedia.org/wiki/Esperanto). - - - -Interoperability between software tools is one of the most important objectives of the SimPhoNy framework. - - -### Abstraction and generalisation -Once a certain degree of interoperability has been reached, other interesting concepts and details that arise: -#### Semantic vs. syntactic - In our day to day life we use a series of sounds, symbols and rules to communicate. - In its simplest level, words have a syntactic side, a role in the sentence they make up, and a explicit meaning. - - However, this is not enough for higher levels of communication and understanding. - If you look at the following sentence: - -

Time flies like an arrow

- - From a syntactic perspective, we can see that _time_ is the subject. A noun. - _Flies_ is the verb, and so on. - If we analyse literally the meaning, we can picture time moving with a motion alike that of an arrow. - - On the other hand, we know the real meaning of the sentence is different. - Here we can deduce that the arrow is used to represent a fast movement, - and that characteristic is then applied to time. This is the semantic knowledge. - - In a semantic approach, we don't just see words as a series of symbols, but as a link to a sign. - And we can apply different rules and behaviours to different concepts because of the properties inherent to each entity. - - If we now look at: - -

Fruit flies like a banana

- - The moment we get to the word _banana_, - we realise something is wrong. Syntactically, this sentence could be equivalent to the previous one. - However, we know that _bananas_ do not usually fly. - And so we decide to change the syntactic interpretation. - _Fruit flies_ becomes the subject, _like_ the verb and _a banana_ the object. - Now it makes sense semantically as well. - - In this semantic realm, the concept of ontologies that will be presented in further sections plays a major role. -#### Requirement simplification - Since we know what a user means from the semantic approach, - we can use this to automatise and simplify the setup and initialisation of processes using default settings. - - For example, a user could decide they want to run a simple simulation, with a certain level of detail - (let's say low, medium or high). - This could be translated into a meaningful initial state that might suffice a general situation. -#### Coupling and linking - In the domain of physics simulations, another interesting use case is coupling and linking. - - For example, a certain engine might be useful for representing structures made up of atomistic particles - (molecular dynamics). - - Another software tool could be focussed on representing bodies of fluids (fluid dynamics). - If both tools can communicate (i.e. there exists some interoperability between them), - they could both be run and synced simultaneously to create more complex scenarios. - - Furthermore, a truly interoperable platform would enable users to store and - access data in databases or other repositories of information. - - - diff --git a/docs/source/ontologies_included.md b/docs/source/ontologies_included.md index 18a69f0..3474e0d 100644 --- a/docs/source/ontologies_included.md +++ b/docs/source/ontologies_included.md @@ -22,14 +22,11 @@ pico install city Take a look at our [examples](jupyter/cuds-api.md) to see how you can build your own city! -## Working with EMMO using OSP-core +## Working with EMMO The second ontology that is ready to be used out of the box is the European -Materials Modelling Ontology, or EMMO in short. This ontology is an effort -to develop an ontology for applied sciences. It is based on physics, -analytical philosophy and information and communication technologies. -Its source code is open and [available on Github](https://github.com/emmo-repo/EMMO). -If you want to develop an emmo compliant ontology, see [the documentation](https://ontology.pages.fraunhofer.de/documentation/latest/). +Materials Modelling Ontology, or EMMO in short. +For a short introduction, see the [fundamentals](./fundamentals.md#emmo). You can install EMMO using [Pico](utils.md#pico-installs-cuds-ontologies). diff --git a/docs/source/overview.md b/docs/source/overview.md new file mode 100644 index 0000000..10f4691 --- /dev/null +++ b/docs/source/overview.md @@ -0,0 +1,118 @@ +# Overview + +SimPhoNy is an ontology-based framework aimed at enabling interoperability between different simulation and data management tools, with a focus on materials science. + + + +## What can SimPhoNy be used for? + +### Manipulate ontology-based linked data, a format well suited for [FAIR data principles](https://en.wikipedia.org/wiki/FAIR_data) + + +[Linked data](https://en.wikipedia.org/wiki/Linked_data) is a format for structured data that facilitates the interoperability among different data sources. In particular, the data is structured as a directed graph, consistent of nodes and labeled arcs. With SimPhoNy, you can not only manipulate this linked data, **but also transform existing non-linked data into linked data**. + +To better understand the idea of linked data, take a quick glance at the toy example below. It shows data about a city from three different data sources: the city's traffic authority, a map from a city guide, and the university registry. As some of the concepts are present in multiple datasets, the linked data representation naturally joins all of them into a single one. + +
+ +![Sample linked data](./_static/img/mergedlinkeddata.svg) + +
+ +_Linked data about a city from three different sources: the city's traffic authority, a map from a city guide, and the university registry. Each data source is represented using a different color and column._ + +
+ +
+ +Although the example above shows just plain linked data, in SimPhoNy, the linked data is enhanced with [ontologies](https://en.wikipedia.org/wiki/Ontology_(information_science)), which give **meaning** to the data. Specifically, SimPhoNy works with ontologies based on the [Web Ontology Language](https://en.wikipedia.org/wiki/Web_Ontology_Language), making the data compatible with the [Semantic Web](https://en.wikipedia.org/wiki/Semantic_Web). + +### Fetch data from a database, run a simulation and immediately store the results + +Ontology-based linked data is not only well suited for the interoperability of data, but also of software tools. In SimPhoNy, one can instantiate individuals from special ontology classes called _wrappers_. These wrappers are in fact a software interface between the core of SimPhoNy (ontology based) and external software tools, disguised to the user as an ontology class. We have already developed wrappers for a few database backends and popular simulation engines for materials science. You can have a look at the existing wrappers on our [GitHub organization](https://github.com/simphony). If needed, you may even consider [developing your own](./wrapper_development.md)! + +As a SimPhoNy user, you can see the data stored in the external software tools transparently as ontology individuals through the wrappers. In this way, moving data between different software tools becomes as simple as moving or copying it from one wrapper to another. + +For example, linked data stored in a SQLite database can be used to run a simulation just by adding the ontology individuals contained in the SQLite wrapper to the Simulation Engine wrapper. Similarly, the ontology individuals representing the results can be simply added back into the database wrapper. + +
+ +![How wrappers work](./_static/img/wrappers.svg) + +
+ +
+ +At this point, the results could be fetched again and for example, visualized with the help of a plotting library. + +
+ +![Toy example of simulation results](./_static/img/trafficsim.svg) + + + +
+ +
+ +

+ +### Couple simulation engines easily + +Exactly in the same way that the data can be moved between a database and a simulation engine using their respective wrappers, it can also be moved between simulation engines. + +This functionality facilitates the coupling and linking between such simulation engines. For example, in the domain of materials science, a certain engine might be useful for representing structures made up of atomistic particles (molecular dynamics), while another software tool could be focussed on representing bodies of fluids (fluid dynamics). As SimPhoNy can enable communication between the two tools, they could both be run and synced simultaneously to create more complex scenarios. + +
+ + + +
+ +The concepts of coupling and linking illustrated in a video. + +
+ +
+ +In order achieve that, it would be necessary to translate the input and output formats of both simulation engines. However, given that the necessary wrappers exist, and their ontologies are compatible, this task becomes relatively simple thanks to SimPhoNy! At the end of the coupling process, just add the results to a database wrapper to store them. + +
+ +![Coupling](./_static/img/wrappers_coupled.svg) + +
+ +_Coupling of two simulation engines, one that handles free traffic and another that exclusively handles traffic lights._ + +
+ +
+ +# Terminology +The name ‘SimPhoNy’ stems from the SimPhoNy EU-project in which it was originally developed (see more details [here](https://www.simphony-project.eu/)). + +Here are some additional terms that are used throughout the documentation: +1. `API`: Application Programming Interface. A set of functions that allow the interaction with an application or system. +1. `OSP`: Open Simulation Platform. + A set of common standards and related tools that form the basic environment on top of which compatible and compliant simulation workflows can be developed and run. + An OSP does not contain any simulation tools itself, it is the common framework enabling to couple and link them. +1. `backend`: a third party application or service. + Simulation engines and databases are examples of backends. +1. `wrapper`: a plugin for OSP-core that adds support to a new backend. + It must allow the user to interact with the backend through the same API as OSP-core. +1. `ontology`: an explicit, formal specification of a shared conceptualization. + In the context of ontology, other relevant terms are: + 1. `class`: a concept. E.g., 'City', 'Experiment'. + 1. `attribute`: a property of a class that has a data type. E.g., 'name' of the type String which could be used as an attribute of 'City'. + 1. `individual`: an instance of a class. E.g., an instance of the class 'City' can be used to represent the city of Freiburg in which case it would have the attribute 'name' with the value 'Freiburg'. + 1. `relationship`: a type of a way in which one individual relates to another. E.g., 'Has-A' which could use to form the relationship 'Freiburg (City) Has-A Dreisam (River)'. + 1. `entity`: a general term that can refer to a class, a relationship, attribute, or an individual. E.g., 'City', 'name', 'Has-A', the Freiburg individual are all entities. + 1. `namespace`: an ontology identifier. E.g., 'city_ontology' which could be used as a namespace for the ontology that consists of the entities 'City', 'name' and 'Has-A'. + - Each entity is uniquely identified by its name and the namespace it is contained in. We call \.\ the `qualified entity name`. +1. `CUDS`: Common Universal Data Structure. A data structure that is used to uniformly represent ontology concepts in programming code. + - CUDS exposes an API that provides CRUD (Create, Read, Update and Delete) functionalities. + - CUDS is a recursive data structure in that a CUDS object may contain other CUDS objects. + - CUDS is the fundamental data type of OSP-core, a framework that establishes interoperability between software systems that are built on top of ontologies. +1. `CUDS class`: represents an ontology class (a concept) and encodes its ontological information. +1. `CUDS object`: is an instance of a CUDS class and represents an ontology individual. diff --git a/docs/source/owl.md b/docs/source/owl.md index f203926..8faf366 100644 --- a/docs/source/owl.md +++ b/docs/source/owl.md @@ -6,6 +6,7 @@ to the following one: ```yaml identifier: emmo ontology_file: https://raw.githubusercontent.com/emmo-repo/EMMO/master/emmo-inferred.owl +format: turtle reference_by_label: True namespaces: mereotopology: http://emmo.info/emmo/top/mereotopology @@ -39,8 +40,13 @@ that contains multiple namespaces. Will be used for uninstallation: `pico uninst **ontology_file**: Path to the inferred owl ontology. That means you should have executed a reasoner on your ontology, e.g. by using the `Export inferred axioms` functionality of [Protégé](https://protege.stanford.edu/). -We support all the formats that [RDFLib](https://rdflib.readthedocs.io/) supports: -RDF/XML, N3, NTriples, N-Quads, Turtle, TriX, RDFa and Microdata. + +**format**: File format of the ontology file to be parsed. We support all the +formats that +[RDFLib](https://rdflib.readthedocs.io/en/stable/plugin_parsers.html) supports: +XML (`xml`, default), RDF/XML (`rdf+xml`), N3 (`n3`), NTriples (`nt`), +N-Quads (`nquads`), Turtle (`turtle`), TriX (`trix`), +RDFa (`rdfa`, `rdfa1.0`, `rdfa1.1`) and Microdata (`mdata`). **reference_by_label** (default False): Whether the label should be used or the IRI suffix to reference entity from within OSP-core. In case of EMMO it is true, because IRI suffixes are not diff --git a/docs/source/utils.md b/docs/source/utils.md index f219ded..8df48d8 100644 --- a/docs/source/utils.md +++ b/docs/source/utils.md @@ -72,6 +72,16 @@ INFO [osp.core.ontology.parser]: Loaded 205 ontology triples in total INFO [osp.core.ontology.installation]: Uninstallation successful ``` +### Ontology installation folder + +The installed ontologies are stored in the directory `~/.osp-ontologies` by +default. On Windows, `~` usually refers to the path +`C:\Users\`. + +The installation directory can be changed by setting the +environment variable `OSP_ONTOLOGIES_DIR`. Such action would move it to +`$OSP_ONTOLOGIES_DIR/.osp-ontologies`. + ### Conflicts with other "pico" installations Some Operating Systems might have a pre-existing tool called _pico_. In most cases, the previous commands should work, but if any problem arises, @@ -181,7 +191,7 @@ _Example:_ ```py >>> Cuds2dot(emmo_town).render() ``` -![cuds2dot sample image](./_static/img/cuds2dot.png) +![](./_static/img/cuds2dot.png "cuds2dot sample image") ### Ontology2Dot _Location:_ console entry point `osp.core.tools.ontology2dot`. @@ -198,7 +208,7 @@ _Example:_ ```sh ontology2dot osp/core/ontology/yml/ontology.city.yml ``` -![ontology2dot sample image](./_static/img/ontology2dot.png) +![](./_static/img/ontology2dot.png "ontology2dot sample image") ## Search @@ -258,3 +268,17 @@ _Examples:_ root=city_cuds, rel=city.hasInhabitant) ``` +## Serialization JSON schema of CUDS objects + +When you serialize a CUDS object using the +[`serialize()` method in the utils module](api_ref.html#osp.core.utils.general.serialize), +you will get a json document as a result. +The method will traverse the hierarchical datastructure +using Depth First Traversal. +Therefore, its result is a json array composed of several flat CUDS objects. + +This array can later be deserialized using the opposite +[`deserialize`](api_ref.html#osp.core.utils.general.deserialize). + +The serialization is done via [JSON-LD](https://json-ld.org/), +with the schema used for the [OSP API in Marketplace](https://gitlab.cc-asp.fraunhofer.de/MarketPlace/osp-api). diff --git a/docs/source/wrapper_development.md b/docs/source/wrapper_development.md index f41cb33..945a159 100644 --- a/docs/source/wrapper_development.md +++ b/docs/source/wrapper_development.md @@ -96,6 +96,94 @@ This allows us to group and clearly define which components should and which one - [Syntactic layer](./detailed_design.md#syntactic-layer): If none is available, one must be developed. +To facilitate the creation of the session class on the interoperability layer, +there are several session abstract base classes that you can make your session +inherit from, which already include some additional generic functions common to a few +typical applications: databases, triplestores and simulation engines. + +On the diagram below, you may observe a simplified session inheritance scheme +for OSP-core. As a wrapper developer, you will most probably want to inherit +from one of following abstract classes: `WrapperSession`, `DbWrapperSession`, +`TripleStoreWrapperSession`, `SqlWrapperSession`, or `SimWrapperSession`. +Your new wrapper session would be located of the OSP-core box, +together among other wrapper sessions like the Simlammps, Sqlite or SqlAlchemy +sessions. + +```eval_rst +.. uml:: + :caption: Simplified session inheritance scheme + :align: center + + rectangle "OSP-core" as OSP { + abstract class Session { + } + + class CoreSession implements Session { + } + + abstract class WrapperSession extends Session { + } + + class TransportSession implements WrapperSession { + } + + abstract class DbWrapperSession extends WrapperSession { + commit() + } + + abstract class TripleStoreWrapperSession extends DbWrapperSession { + } + + abstract class SqlWrapperSession extends TripleStoreWrapperSession { + } + + abstract class SimWrapperSession extends WrapperSession { + run() + } + } + + rectangle "Sqlite wrapper" as sqlite { + class SqliteWrapperSession implements SqlWrapperSession { + } + } + + rectangle "SqlAlchemy wrapper" as sqlalchemy { + class SqlAlchemyWrapperSession implements SqlWrapperSession { + } + } + + rectangle "Your wrapper" as yourwrapper { + class YourSession{ + } + } + + rectangle "Simlammps wrapper" as simlammps { + class SimlammpsSession implements SimWrapperSession { + } + } + + ' ----------------------- + ' -------- NOTES -------- + ' ----------------------- + note as OSP.note_core + The CoreSession is the default + shared session for all Python objects + end note + OSP.note_core .. CoreSession + + note as note_db + The db changes are persisted via + cuds_object.session.commit() + end note + note_db .. DbWrapperSession + + note as OSP.note_sim + The simulation is run by calling + cuds_object.session.run() + end note + OSP.note_sim .. SimWrapperSession +``` + ## Engine installation Most engines will require some sort of compilation or installation before being able to use them through Python. @@ -201,7 +289,7 @@ the `Dockerfile` should be installed via the provided `docker_install.sh` script It will tag the OSP-core image and call the Dockerfile in the root of the wrapper accordingly. In terms of implementation, a wrapper developer needs to take care of the `Dockerfile`, -making sure to leave the first two lines as they are in the [wrapper development repo](https://gitlab.cc-asp.fraunhofer.de/simphony/wrappers/wrapper-development/blob/master/Dockerfile). +making sure to leave the first two lines as they are in the [wrapper development repo](https://github.com/simphony/wrapper-development/blob/master/Dockerfile). `docker_install.sh` will only have to be modified with the proper tag for the wrapper image. ## Continuous Integration diff --git a/docs/source/yaml.md b/docs/source/yaml.md index abb0c0c..6fba7a6 100644 --- a/docs/source/yaml.md +++ b/docs/source/yaml.md @@ -1,4 +1,4 @@ -# Description of YAML format Specification +# Description of YAML format specification This file describes how you can create ontologies using YAML. diff --git a/local_build.Dockerfile b/local_build.Dockerfile new file mode 100644 index 0000000..baf0e63 --- /dev/null +++ b/local_build.Dockerfile @@ -0,0 +1,21 @@ +FROM python:3.8-buster + +RUN apt-get update +RUN apt-get install -y pandoc default-jre graphviz +RUN apt-get install -y texlive-latex-recommended \ + texlive-latex-extra \ + texlive-fonts-recommended \ + latexmk + +WORKDIR /app +ADD . . + +RUN pip install -r requirements.txt + +CMD sphinx-autobuild --host 0.0.0.0 docs/source docs/build/html + +# Build: +# $ docker build -f local_build.Dockerfile -t simphony-docs . + +# Run: +# $ docker run -it --rm -v $PWD:/app -p 8000:8000 simphony-docs diff --git a/packageinfo.py b/packageinfo.py index eccffba..3c6c186 100644 --- a/packageinfo.py +++ b/packageinfo.py @@ -1,4 +1,2 @@ NAME = "simphony_docs" -VERSION = "2.3.0" -OSP_CORE_MIN = "3.4.0" -OSP_CORE_MAX = "3.5.0" +VERSION = "2.4.0" diff --git a/requirements.txt b/requirements.txt index 30c4683..285a2ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ git+https://github.com/simphony/osp-core.git#egg=osp-core -sphinx -recommonmark -sphinx_rtd_theme -sphinxcontrib-plantuml -nbsphinx -sphinx-copybutton -ipython -jupyter -sphinx-jsonschema - +sphinx==3.5.3 +recommonmark==0.7.1 +sphinx_rtd_theme==0.5.2 +sphinxcontrib-plantuml==0.20.1 +nbsphinx==0.8.2 +sphinx-copybutton==0.3.1 +ipython==7.22.0 +jupyter==1.0.0 +sphinx-autobuild==2021.3.14 +sphinx-panels==0.5.2 diff --git a/setup.py b/setup.py index d791c82..dc5168d 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,13 @@ import os import subprocess from setuptools import setup -from sphinx.setup_command import BuildDoc -from packageinfo import VERSION, NAME, OSP_CORE_MIN, OSP_CORE_MAX +from packageinfo import VERSION, NAME -source_dir = os.path.join('docs', 'source') -build_dir = os.path.join('docs', 'build') - -# Create custom build that also compiles the LaTeX -class CustomBuild(BuildDoc): - def run(self): - super().run() - path = os.path.join(build_dir, 'latex') - subprocess.check_call(["make", "-C", path]) +def generate_pdf(self): + path = os.path.join('docs', 'build', 'latex') + subprocess.check_call(["make", "-C", path]) # Read description @@ -25,18 +18,8 @@ def run(self): setup( name=NAME, version=VERSION, - author='Materials Data Science and Informatics team at Fraunhofer IWM', + author='Materials Data Science and Informatics Team at Fraunhofer IWM', description='The SimPhoNy documentation', - keywords='simphony, documentation, sphinx, cuds, Fraunhofer IWM', + keywords='simphony, documentation, sphinx, Fraunhofer IWM', long_description=README_TEXT, - cmdclass={'install': CustomBuild}, - command_options={ - 'install': { - 'version': ('setup.py', VERSION), - 'release': ('setup.py', VERSION), - 'source_dir': ('setup.py', source_dir), - 'build_dir': ('setup.py', build_dir), - 'builder': ('setup.py', 'html, latex'), - }, - }, )