{ "cells": [ { "cell_type": "markdown", "id": "cb21fe49", "metadata": {}, "source": [ ":::{important}\n", "\n", "The \"Querying for data\" module is written in a Jupyter notebook format.\n", "On this page you can find a rendered version of the notebook, but you can download the `.ipynb` file using the following command:\n", "\n", "```{code-block} console\n", "$ wget https://filedn.com/lsOzB8TTUIDz2WkFj8o6qhp/permanent/querying.ipynb\n", "```\n", "\n", "**Note that not all cells are complete!**\n", "Many are exercises for you to solve.\n", "\n", ":::\n", "\n", "# Querying for data\n", "\n", "The notebook will show you how the `QueryBuilder` can be used to query your database for specific data.\n", "It will demonstrate certain concepts you can then use to perform certain queries on your own database.\n", "Some of these question cells will have partial solutions that you will have to complete.\n", "\n", "```{important}\n", "Make sure to execute the cell below this one (it may be hidden)\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "id": "e2522aa3", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:51.669998Z", "iopub.status.busy": "2021-07-06T07:49:51.669255Z", "iopub.status.idle": "2021-07-06T07:49:53.211885Z", "shell.execute_reply": "2021-07-06T07:49:53.212252Z" }, "tags": [ "hide-input", "hide-output" ] }, "outputs": [], "source": [ "from IPython.display import Image\n", "from datetime import datetime, timedelta\n", "import numpy as np\n", "from aiida import load_profile\n", "from matplotlib import gridspec, pyplot as plt\n", "load_profile()\n", "from aiida.orm import load_node, Node, Group, Computer, User, CalcJobNode, Code\n", "from aiida.plugins import CalculationFactory, DataFactory\n", "\n", "PwCalculation = CalculationFactory('quantumespresso.pw')\n", "StructureData = DataFactory('structure')\n", "KpointsData = DataFactory('array.kpoints')\n", "Dict = DataFactory('dict')\n", "UpfData = DataFactory('upf')\n", "\n", "def plot_results(query_res):\n", " \"\"\"\n", " :param query_res: The result of an instance of the QueryBuilder\n", " \"\"\"\n", " smearing_unit_set,magnetization_unit_set,pseudo_family_set = set(), set(), set()\n", " # Storing results:\n", " results_dict = {}\n", " for pseudo_family, formula, smearing, smearing_units, mag, mag_units in query_res:\n", " if formula not in results_dict:\n", " results_dict[formula] = {}\n", " # Storing the results:\n", " results_dict[formula][pseudo_family] = (smearing, mag)\n", " # Adding to the unit set:\n", " smearing_unit_set.add(smearing_units)\n", " magnetization_unit_set.add(mag_units)\n", " pseudo_family_set.add(pseudo_family)\n", "\n", " # Sorting by formula:\n", " sorted_results = sorted(results_dict.items())\n", " formula_list = next(zip(*sorted_results))\n", " nr_of_results = len(formula_list)\n", "\n", " # Checks that I have not more than 3 pseudo families.\n", " # If more are needed, define more colors\n", " #pseudo_list = list(pseudo_family_set)\n", " if len(pseudo_family_set) > 3:\n", " raise Exception('I was expecting 3 or less pseudo families')\n", "\n", " colors = ['b', 'r', 'g']\n", "\n", " # Plotting:\n", " plt.clf()\n", " fig=plt.figure(figsize=(16, 9), facecolor='w', edgecolor=None)\n", " gs = gridspec.GridSpec(2,1, hspace=0.01, left=0.1, right=0.94)\n", "\n", " # Defining barwidth\n", " barwidth = 1. / (len(pseudo_family_set)+1)\n", " offset = [-0.5+(0.5+n)*barwidth for n in range(len(pseudo_family_set))]\n", " # Axing labels with units:\n", " yaxis = (\"Smearing energy [{}]\".format(smearing_unit_set.pop()),\n", " \"Total magnetization [{}]\".format(magnetization_unit_set.pop()))\n", " # If more than one unit was specified, I will exit:\n", " if smearing_unit_set:\n", " raise ValueError('Found different units for smearing')\n", " if magnetization_unit_set:\n", " raise ValueError('Found different units for magnetization')\n", "\n", " # Making two plots, the top one for the smearing, the bottom one for the magnetization\n", " for index in range(2):\n", " ax=fig.add_subplot(gs[index])\n", " for i,pseudo_family in enumerate(pseudo_family_set):\n", " X = np.arange(nr_of_results)+offset[i]\n", " Y = np.array([thisres[1][pseudo_family][index] for thisres in sorted_results])\n", " ax.bar(X, Y, width=0.2, facecolor=colors[i], edgecolor=colors[i], label=pseudo_family)\n", " ax.set_ylabel(yaxis[index], fontsize=14, labelpad=15*index+5)\n", " ax.set_xlim(-0.5, nr_of_results-0.5)\n", " ax.set_xticks(np.arange(nr_of_results))\n", " if index == 0:\n", " plt.setp(ax.get_yticklabels()[0], visible=False)\n", " ax.xaxis.tick_top()\n", " ax.legend(loc=3, prop={'size': 18})\n", " else:\n", " plt.setp(ax.get_yticklabels()[-1], visible=False)\n", " for i in range(0, nr_of_results, 2):\n", " ax.axvspan(i-0.5, i+0.5, facecolor='y', alpha=0.2)\n", " ax.set_xticklabels(list(formula_list),rotation=90, size=14, ha='center')\n", " plt.show()\n", "\n", "def generate_query_graph(qh, out_file_name):\n", "\n", " def draw_vertice_settings(idx, vertice, **kwargs):\n", " \"\"\"\n", " Returns a string with all infos needed in a .dot file to define a node of a graph.\n", " :param node:\n", " :param kwargs: Additional key-value pairs to be added to the returned string\n", " :return: a string\n", " \"\"\"\n", " if vertice['entity_type'].startswith('process'):\n", " shape = \"shape=polygon,sides=4\"\n", " elif vertice['entity_type'].startswith('data.code'):\n", " shape = \"shape=diamond\"\n", " else:\n", " shape = \"shape=ellipse\"\n", " filters = kwargs.pop('filters', None)\n", " additional_string = \"\"\n", " if filters:\n", " additional_string += '\\nFilters:'\n", " for k,v in filters.items():\n", " additional_string += \"\\n {} : {}\".format(k,v)\n", "\n", "\n", " label_string = \" ('{}')\".format(vertice['tag'])\n", "\n", " labelstring = 'label=\"{} {}{}\"'.format(\n", " vertice['entity_type'], #.split('.')[-2] or 'Node',\n", " label_string,\n", " additional_string)\n", " #~ return \"N{} [{},{}{}];\".format(idx, shape, labelstring,\n", " return \"{} [{},{}];\".format(vertice['tag'], shape, labelstring)\n", " nodes = {v['tag']:draw_vertice_settings(idx, v, filters=qh['filters'][v['tag']]) for idx, v in enumerate(qh['path'])}\n", " links = [(v['tag'], v['joining_value'], v['joining_keyword']) for v in qh['path'][1:]]\n", "\n", " with open('temp.dot','w') as fout:\n", " fout.write(\"digraph G {\\n\")\n", " for l in links:\n", " fout.write(' {} -> {} [label=\" {}\"];\\n'.format(*l))\n", " for _, n_values in nodes.items():\n", " fout.write(\" {}\\n\".format(n_values))\n", "\n", " fout.write(\"}\\n\")\n", " import os\n", " os.system('dot temp.dot -Tpng -o {}'.format(out_file_name))\n", "\n", "def store_formula_in_extra():\n", " from aiida.orm import QueryBuilder\n", " query = QueryBuilder()\n", " query.append(StructureData, filters={'extras':{'!has_key':'formula'}})\n", " for structure, in query.iterall():\n", " structure.set_extra('formula', structure.get_formula(mode='count'))\n", "\n", "store_formula_in_extra()" ] }, { "cell_type": "markdown", "id": "60e5647f", "metadata": {}, "source": [ "## Introduction to the QueryBuilder\n", "\n", "As you will use AiiDA to submit and manage your calculations, the database that stores all the data and the provenance will quickly grow to be very large.\n", "To help you find the needle you might be looking for in this big haystack, you need an efficient search tool.\n", "AiiDA provides a tool to do exactly this: the {class}`~aiida.orm.querybuilder.QueryBuilder`.\n", "The `QueryBuilder` acts as the gatekeeper to your database, to whom you can ask questions about the contents of your database (also referred to as queries), by specifying what you are looking for.\n", "In this part of the tutorial, we will focus on how to use the `QueryBuilder` to make these queries and understand and use the results.\n", "\n", "In order to use the `QueryBuilder`, you first need to import it.\n", "This can accomplished by executing the `import` statement in the following cell.\n", "Go ahead and select the next cell, and press `Shift+Enter`." ] }, { "cell_type": "code", "execution_count": 2, "id": "5d395ed5", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.216148Z", "iopub.status.busy": "2021-07-06T07:49:53.215624Z", "iopub.status.idle": "2021-07-06T07:49:53.217619Z", "shell.execute_reply": "2021-07-06T07:49:53.218156Z" } }, "outputs": [], "source": [ "from aiida.orm import QueryBuilder" ] }, { "cell_type": "markdown", "id": "24c0fae3", "metadata": {}, "source": [ "Before you can use the `QueryBuilder` to query the database, you first need to create an instance of it:" ] }, { "cell_type": "code", "execution_count": 3, "id": "8a44d5a9", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.221750Z", "iopub.status.busy": "2021-07-06T07:49:53.221212Z", "iopub.status.idle": "2021-07-06T07:49:53.222977Z", "shell.execute_reply": "2021-07-06T07:49:53.223336Z" } }, "outputs": [], "source": [ "query = QueryBuilder()" ] }, { "cell_type": "markdown", "id": "3442e00b", "metadata": {}, "source": [ "Now that you have an instance of the `QueryBuilder` named `query`, you are ready to start asking about the content of your database.\n", "For example, we may want to know exactly how many nodes there are in the database.\n", "To let the AiiDA database know that we are interested in all the occurrences of the `Node` class, you can `append` it to the list of objects it should find through `query`.\n", "\n", "```{note}\n", "The method is called `append` because, as we will see later, you can append multiple nodes to a `QueryBuilder` instance consecutively to search in the graph, as if you had a list.\n", "What we are doing is querying a graph, and for every vertex of the graph in our sub-query, we will use one `append` call.\n", "But this use will be demonstrated more fully in a few steps.\n", "```" ] }, { "cell_type": "code", "execution_count": 4, "id": "045892c2", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.228592Z", "iopub.status.busy": "2021-07-06T07:49:53.228075Z", "iopub.status.idle": "2021-07-06T07:49:53.230689Z", "shell.execute_reply": "2021-07-06T07:49:53.231057Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query.append(Node)" ] }, { "cell_type": "markdown", "id": "7cf5a3fc", "metadata": {}, "source": [ "We have now narrowed down the scope of `query` to just the nodes that are present in the database (i.e., we are ignoring computers, users, etc.).\n", "To learn how many nodes there are exactly, you can use the `count()` method:" ] }, { "cell_type": "code", "execution_count": 5, "id": "7306fc33", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.238013Z", "iopub.status.busy": "2021-07-06T07:49:53.237455Z", "iopub.status.idle": "2021-07-06T07:49:53.241438Z", "shell.execute_reply": "2021-07-06T07:49:53.241823Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "1981" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query.count()" ] }, { "cell_type": "markdown", "id": "749f4236", "metadata": {}, "source": [ "Now as you may have learned in previous sections of the tutorial, nodes come in different kinds and flavors.\n", "For example, all the crystal structures stored in the database are saved in nodes that are of the type `StructureData`.\n", "If instead of all the nodes, we would rather like to count only the crystal structure nodes, we simply tell a `QueryBuilder` instance to narrow its scope only to objects of type `StructureData`.\n", "Since we want to create a new independent query, you must create a new instance of the `QueryBuilder`.\n", "\n", "### Exercise\n", "\n", "In the next cell, we have typed part of the code to count all the structure nodes.\n", "See if you can finish the line with the comment, to tell the `QueryBuilder` that you are only interested in `StructureData` nodes." ] }, { "cell_type": "code", "execution_count": 6, "id": "bc043e92", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.245904Z", "iopub.status.busy": "2021-07-06T07:49:53.245289Z", "iopub.status.idle": "2021-07-06T07:49:53.545344Z", "shell.execute_reply": "2021-07-06T07:49:53.545747Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "InputValidationError", "evalue": "You need to specify at least a class or a entity_type", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInputValidationError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/163535632.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mquery\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mQueryBuilder\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----> 2\u001b[0;31m \u001b[0mquery\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# How do we finish this line to tell the query builder to count only the structure nodes?\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcount\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~/Documents/GitHub/aiida-tutorials/.tox/notebooks/lib/python3.9/site-packages/aiida/orm/querybuilder.py\u001b[0m in \u001b[0;36mappend\u001b[0;34m(self, cls, entity_type, tag, filters, project, subclassing, edge_tag, edge_filters, edge_project, outerjoin, **kwargs)\u001b[0m\n\u001b[1;32m 664\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 665\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mentity_type\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 666\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mInputValidationError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'You need to specify at least a class or a entity_type'\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 667\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 668\u001b[0m \u001b[0;31m# Let's check if it is a valid class or type\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mInputValidationError\u001b[0m: You need to specify at least a class or a entity_type" ] } ], "source": [ "query = QueryBuilder()\n", "query.append() # How do we finish this line to tell the query builder to count only the structure nodes?\n", "query.count()" ] }, { "cell_type": "markdown", "id": "c605c5b7", "metadata": {}, "source": [ "Instead of just counting how many crystal structure nodes exist, we may also actually want to see some of them.\n", "This is as easy as telling the `QueryBuilder` that we are not interested in the `count` but rather that we want to retrieve `all` the nodes." ] }, { "cell_type": "code", "execution_count": 7, "id": "e7dc7e30", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.551708Z", "iopub.status.busy": "2021-07-06T07:49:53.551141Z", "iopub.status.idle": "2021-07-06T07:49:53.622861Z", "shell.execute_reply": "2021-07-06T07:49:53.623286Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " []]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(StructureData)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "01ecf5bf", "metadata": {}, "source": [ "Note that this command is very literal and does in fact retrieve **all** the crystal structure nodes that are stored in the database, which may be very slow if your database becomes very large.\n", "One solution is to tell the `QueryBuilder` that we are, for example, only interested in 5 crystal structure nodes.\n", "This can be done with the `limit()` method as follows:" ] }, { "cell_type": "code", "execution_count": 8, "id": "c52cdac0", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.630223Z", "iopub.status.busy": "2021-07-06T07:49:53.629553Z", "iopub.status.idle": "2021-07-06T07:49:53.634941Z", "shell.execute_reply": "2021-07-06T07:49:53.635417Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[],\n", " [],\n", " [],\n", " [],\n", " []]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(StructureData)\n", "query.limit(5)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "8d00be13", "metadata": {}, "source": [ "Another option is to use the concept of *array slicing*, native to Python, to specify only to return a subset of the total return set.\n", "Notice that this example can be very slow in big databases.\n", "When you want performance, use the functionality native to the `QueryBuilder`, like `limit`, which limits the number of results directly at the database level!\n", "\n", "The following will return the first 7 results." ] }, { "cell_type": "code", "execution_count": 9, "id": "83cea0b0", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.640312Z", "iopub.status.busy": "2021-07-06T07:49:53.639748Z", "iopub.status.idle": "2021-07-06T07:49:53.745631Z", "shell.execute_reply": "2021-07-06T07:49:53.745237Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[],\n", " [],\n", " [],\n", " [],\n", " [],\n", " [],\n", " []]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query.limit(None)\n", "query.all()[:7]" ] }, { "cell_type": "markdown", "id": "d6b7e610", "metadata": {}, "source": [ "If you want to know a little bit more about the retrieved crystal structure nodes, you can loop through the returned results by using the `iterall()` method, short for \"iterate over all\".\n", "This allows you, for instance, to print the formula of the structures:" ] }, { "cell_type": "code", "execution_count": 10, "id": "b8b0c318", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.752073Z", "iopub.status.busy": "2021-07-06T07:49:53.751436Z", "iopub.status.idle": "2021-07-06T07:49:53.767596Z", "shell.execute_reply": "2021-07-06T07:49:53.767965Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "O3TaTl\n", "O3Sn2\n", "NiO3Sr\n", "O3PbZr\n", "AlO3Y\n" ] } ], "source": [ "query = QueryBuilder()\n", "query.append(StructureData)\n", "query.limit(5)\n", "for structure, in query.iterall():\n", " print(structure.get_formula())" ] }, { "cell_type": "markdown", "id": "18eeed55", "metadata": {}, "source": [ "This is just a simple example how we can employ the `QueryBuilder` to get details about the contents of our database.\n", "We have now seen simple queries for the `Node` and `StructureData` classes, but the same rules apply to all the AiiDA `Node` sub-classes.\n", "For example, you may want to count the number of entries for each of the `Node` sub-classes in the following list, as well as the `Node` class itself:" ] }, { "cell_type": "code", "execution_count": 11, "id": "389ff51f", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.771647Z", "iopub.status.busy": "2021-07-06T07:49:53.771146Z", "iopub.status.idle": "2021-07-06T07:49:53.772979Z", "shell.execute_reply": "2021-07-06T07:49:53.773359Z" } }, "outputs": [], "source": [ "class_list = [Node, StructureData, KpointsData, Dict, UpfData, Code]" ] }, { "cell_type": "markdown", "id": "57402f66", "metadata": {}, "source": [ "### Exercise\n", "\n", "Using the tools you have learned so far, it is possible to build a table of the number of occurrences of each of these `Node` classes that are stored in the database.\n", "You can loop over the `class_list` list and create a `QueryBuilder` instance for each `Node` (sub-)class.\n", "See if you can finish the following loop by completing the line with the comment, printing the count of each `Node` (sub-)class." ] }, { "cell_type": "code", "execution_count": 12, "id": "574f37d3", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.777964Z", "iopub.status.busy": "2021-07-06T07:49:53.777364Z", "iopub.status.idle": "2021-07-06T07:49:53.779797Z", "shell.execute_reply": "2021-07-06T07:49:53.780179Z" }, "tags": [ "remove-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "\n", "\n", "\n", "\n" ] } ], "source": [ "for class_name in class_list:\n", " query = QueryBuilder()\n", " query.append(class_name)\n", " print() # Finish this line to print the results!" ] }, { "cell_type": "markdown", "id": "bc474aca", "metadata": {}, "source": [ "If all went well, you should see something like the following, where of course the numbers may differ for your database:\n", "\n", "Class name | Entries\n", "---------------|--------\n", " Node | 10273\n", " StructureData | 271\n", " KpointsData | 953\n", " Dict | 2922\n", " UpfData | 85\n", " Code | 10" ] }, { "cell_type": "markdown", "id": "39be6173", "metadata": {}, "source": [ "(query/project)=\n", "\n", "## Projection and filters\n", "\n", "Up until now we have always asked the `QueryBuilder` instances to return complete nodes.\n", "However, we might not necessarily be interested in all the node's properties, but rather just a selected set or even just a single property.\n", "We can tell the `QueryBuilder` which properties we would like to be returned, by asking it to **project** those properties in the result.\n", "For example, you may only want to get the universally unique identifiers (UUIDs) of a set of nodes, which is stored in the `uuid` attribute." ] }, { "cell_type": "code", "execution_count": 13, "id": "96d00a36", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.785952Z", "iopub.status.busy": "2021-07-06T07:49:53.785371Z", "iopub.status.idle": "2021-07-06T07:49:53.788936Z", "shell.execute_reply": "2021-07-06T07:49:53.789367Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[['7f079d70-e361-426a-af77-4fd86de8be88'],\n", " ['fc9a76c1-f07d-41ac-98c8-d81c5118e351'],\n", " ['b66bf73b-8b26-498b-815d-28bcd5fd52df'],\n", " ['7c5371ee-632d-4f08-be55-ee30587b9561'],\n", " ['b9bf60b7-3c56-4ccc-9b24-03ddedd77f68']]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(Node, project=['uuid'])\n", "query.limit(5)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "af7e59bf", "metadata": {}, "source": [ "By using the `project` keyword in the `append` call, you are specifying `query` to inform AiiDA that you are only interested in the `uuid` property of the `Node` class.\n", "Note that the value assigned to `project` is a list, since we may want to specify more than one property.\n", "\n", "### Exercise\n", "\n", "See if you can get the `QueryBuilder` to return *both* the PK and the UUID of the first 5 nodes in the following cell.\n", "\n", "```{important}\n", "In the context of the `QueryBuilder`, the PK of a node is called `id` and the UUID is called `uuid` (as seen above).\n", "```" ] }, { "cell_type": "code", "execution_count": 14, "id": "879e439c", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.793051Z", "iopub.status.busy": "2021-07-06T07:49:53.792494Z", "iopub.status.idle": "2021-07-06T07:49:53.795346Z", "shell.execute_reply": "2021-07-06T07:49:53.795724Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "SyntaxError", "evalue": "expression cannot contain assignment, perhaps you meant \"==\"? (901758042.py, line 2)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/901758042.py\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m query.append(Node, project=)#? What should the value be for the project key\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m expression cannot contain assignment, perhaps you meant \"==\"?\n" ] } ], "source": [ "query = QueryBuilder()\n", "query.append(Node, project=)#? What should the value be for the project key\n", "query.limit(5)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "c41eb3bf", "metadata": {}, "source": [ "To give you an idea of the various properties you can project for some of the base AiiDA classes you can consult the following table.\n", "Note that this is by no means an exhaustive list:\n", "\n", "Class | Properties\n", "---------|-----------\n", "Node | `id`, `uuid`, `node_type`, `label`, `description`, `ctime`, `mtime`\n", "Computer | `id`, `uuid`, `name`, `hostname`, `description`, `transport_type`, `scheduler_type`\n", "User | `id`, `email`, `first_name`, `last_name`, `institution`\n", "Group | `id`, `uuid`, `label`, `type_string`, `time`, `description`" ] }, { "cell_type": "markdown", "id": "b2b394a4", "metadata": {}, "source": [ "The same properties can also be used to *filter* for specific nodes in your database.\n", "Indeed, up until now, you have only asked the `QueryBuilder` to return *all* the instances of a certain type of node, or at best a limited number of those (without specifying which ones).\n", "But in general we might be interested in a very specific node.\n", "For example, we may have the PK of a certain node and we would like to know when it was created and last modified.\n", "You can tell the `QueryBuilder` instance to select nodes that only match that criterion, by telling it to **filter** based on that property." ] }, { "cell_type": "code", "execution_count": 15, "id": "76210dad", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.801908Z", "iopub.status.busy": "2021-07-06T07:49:53.801366Z", "iopub.status.idle": "2021-07-06T07:49:53.805203Z", "shell.execute_reply": "2021-07-06T07:49:53.805597Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[datetime.datetime(2014, 10, 28, 20, 18, 53, 927563, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=60, name=None)),\n", " datetime.datetime(2014, 10, 28, 20, 18, 54, 388764, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=60, name=None))]]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(Node, project=['ctime', 'mtime'], filters={'id': {'==': 1}})\n", "query.all()" ] }, { "cell_type": "markdown", "id": "aa89c540", "metadata": {}, "source": [ "Note the syntax of the `filters` keyword.\n", "The value is a dictionary, where the keys indicate the node property it operates on, in this case the `id` property, represeting the node's PK.\n", "The value of that key is again itself a dictionary, where the key indicates the logical operator *EQUAL TO* via two equality signs (`==`), and the value corresponds to the desired value of the property.\n", "\n", "You may have multiple criteria that you want to filter for, in which case you can use the logical `or` and `and` operators.\n", "Let's say, for example, you want the `QueryBuilder` to retrieve all the crystal structure nodes (`StructureData`) that were created no longer than 12 days ago **and** have an `a` in their UUID.\n", "You can express this criterion by making use of the `and` operator, which allows you to specify multiple filters that all have to be satisfied." ] }, { "cell_type": "code", "execution_count": 16, "id": "8afa0005", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.813196Z", "iopub.status.busy": "2021-07-06T07:49:53.812564Z", "iopub.status.idle": "2021-07-06T07:49:53.816377Z", "shell.execute_reply": "2021-07-06T07:49:53.817353Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from datetime import datetime, timedelta\n", "\n", "query = QueryBuilder()\n", "query.append(\n", " StructureData,\n", " filters={\n", " 'and': [\n", " {'ctime': {'>': datetime.now() - timedelta(days=12)}},\n", " {'uuid': {'like': '%a%'}}\n", " ]\n", " }\n", ")\n", "query.all()" ] }, { "cell_type": "markdown", "id": "bb2e4e99", "metadata": {}, "source": [ "You may have noticed that the _greater than_ (`>`) operator, and its related operators, can work with Python `datetime` objects.\n", "These are just a few of the operators that `QueryBuilder` understands.\n", "Below you find a table with some of the logical operators that you can use:\n", "\n", "Operator | Data type | Example | Description\n", "---------------------|-----------------------|------------------------------------|------------------\n", "`==` | All | `{'==': '12'}` | Equality operator\n", "`in` | All | `{'in':['FINISHED', 'PARSING']}` | Member of a set\n", "`<`, `>`, `<=`, `>=` | float, int, datetime | `{'>': 5.2}` | Size comparison operator\n", "`like` | char, str | `{'like': 'calculation%'}` | String comparison, `%` is a wildcard\n", "`ilike` | char, str | `{'ilike': 'caLCulAtion%'}` | String comparison, capitalization insensitive\n", "`or` | | `{'or': [{'<': 5.3}, {'>': 6.3}]}` | Logical *OR* operator\n", "`and` | | `{'and': [{'>=': 2}, {'<=': 6}]}` | Logical *AND* operator" ] }, { "cell_type": "markdown", "id": "e8c504ab", "metadata": {}, "source": [ "### Exercise\n", "\n", "Try to write a query below that will retrieve all `Group` nodes whose `label` property starts with the string `tutorial`." ] }, { "cell_type": "code", "execution_count": 17, "id": "e4fdb8d4", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.821212Z", "iopub.status.busy": "2021-07-06T07:49:53.820690Z", "iopub.status.idle": "2021-07-06T07:49:53.822435Z", "shell.execute_reply": "2021-07-06T07:49:53.822801Z" } }, "outputs": [], "source": [ "# Write your query here" ] }, { "cell_type": "markdown", "id": "56d5e34e", "metadata": {}, "source": [ "## Defining relationships between query clauses\n", "\n", "So far we have seen how the `QueryBuilder` can be used to search the database for entries of a specific node type, potentially projecting only specific properties and filtering for certain property values.\n", "However, our nodes do not live in a vacuum.\n", "They are part of a directed acyclic graph and are thus linked to one another.\n", "Therefore, we typically want to be able to search for nodes based on a certain relationship that they might have with other nodes.\n", "Consider for example that you have a `StructureData` node that was produced by some calculation.\n", "How would you retrieve the calculation, while only having knowledge of the `StructureData` node?\n", "\n", "To accomplish this, you need to be able to tell the `QueryBuilder` what the relationship is between the nodes you are interested in.\n", "With the `QueryBuilder`, the following can be done to find all the crystal structure nodes that have been created as an output by a `PwCalculation` process.\n", "\n", "```{important}\n", "In the graph, we are not looking for a `PwCalculation` process (since processes do not live in the graph, as you have learned previously).\n", "We are actually looking for a `CalcJobNode` whose `process_type` property indicates it was run by a `PwCalculation` process.\n", "Since this is a very common pattern, the `QueryBuilder` allows to directly append the `PwCalculation` process class as a short-cut, but it internally unwraps this into a query for a `CalcJobNode` with the appropriate filter on the `process_type` property.\n", "```" ] }, { "cell_type": "code", "execution_count": 18, "id": "dfffe219", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.828290Z", "iopub.status.busy": "2021-07-06T07:49:53.827655Z", "iopub.status.idle": "2021-07-06T07:49:53.830642Z", "shell.execute_reply": "2021-07-06T07:49:53.830098Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(PwCalculation, tag='calculation')" ] }, { "cell_type": "markdown", "id": "487fa512", "metadata": {}, "source": [ "Since we are looking for pairs of nodes, you need to `append` the second node as well to the `QueryBuilder` instance, `query`.\n", "In the second line above, to specify the relationship between the nodes, we need to be able to reference back to the `CalcJobNode` that is matched.\n", "Therefore, you gave it a tag with the `tag` keyword.\n", "This can now be used in the following line:" ] }, { "cell_type": "code", "execution_count": 19, "id": "797a6b35", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.834684Z", "iopub.status.busy": "2021-07-06T07:49:53.834135Z", "iopub.status.idle": "2021-07-06T07:49:53.836485Z", "shell.execute_reply": "2021-07-06T07:49:53.836854Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query.append(StructureData, with_incoming='calculation')" ] }, { "cell_type": "markdown", "id": "04abbc5f", "metadata": {}, "source": [ "The goal was to find `StructureData` nodes, so we `append` that to the `query`.\n", "However, we didn't want to find just any `StructureData` nodes; they had to be an output of `PwCalculation`.\n", "\n", "Note how you expressed this relation by the `with_incoming` keyword, because we want a `StructureData` node having an *incoming* link from the `CalcJobNode` referenced by the `'calculation'` tag (i.e., the `StructureData` must be an *output* of the calculation).\n", "\n", "What remains to do is execute the query:" ] }, { "cell_type": "code", "execution_count": 20, "id": "90d896ac", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.844603Z", "iopub.status.busy": "2021-07-06T07:49:53.844015Z", "iopub.status.idle": "2021-07-06T07:49:53.852392Z", "shell.execute_reply": "2021-07-06T07:49:53.852750Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[],\n", " [],\n", " [],\n", " [],\n", " []]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query.limit(5)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "a0cc7c32", "metadata": {}, "source": [ "What you have done can be visualized schematically, thanks to a little tool included in the very first notebook cell (i.e., if the following doesn't work, you should re-run the very first cell and try again)." ] }, { "cell_type": "code", "execution_count": 21, "id": "28b831b3", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.856701Z", "iopub.status.busy": "2021-07-06T07:49:53.856127Z", "iopub.status.idle": "2021-07-06T07:49:53.983266Z", "shell.execute_reply": "2021-07-06T07:49:53.983646Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/chrisjsewell/Documents/GitHub/aiida-tutorials/.tox/notebooks/lib/python3.9/site-packages/aiida/orm/querybuilder.py:1839: AiidaDeprecationWarning: method is deprecated, use the `queryhelp` property instead\n", " warnings.warn('method is deprecated, use the `queryhelp` property instead', AiidaDeprecationWarning)\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "generate_query_graph(query.get_json_compatible_queryhelp(), 'query1.png')\n", "Image(filename='query1.png')" ] }, { "cell_type": "markdown", "id": "c6ded78c", "metadata": {}, "source": [ "The `with_incoming` keyword is only one of many potential relationships that exist between the various AiiDA nodes and that are implemented in the `QueryBuilder`.\n", "The table below gives an overview of the implemented relationships, which nodes they are defined for and what relation it implicates.\n", "The full list of relations can be found {ref}`on this page of the AiiDA documentation`." ] }, { "cell_type": "markdown", "id": "0c2d7141", "metadata": {}, "source": [ "Entity from | Entity to | Relationship | Explanation\n", "------------|-----------|------------------|------------\n", "Node | Node | with_outgoing | One node as input of another node\n", "Node | Node | with_incoming | One node as output of another node\n", "Node | Node | with_descendants | One node as the ancestor of another node\n", "Node | Node | with_ancestors | One node as descendant of another node\n", "Group | Node | with_node | The group of a node\n", "Node | Group | with_group | The node is a member of a group\n", "Computer | Node | with_node | The computer of a node\n", "Node | Computer | with_computer | The node of a computer\n", "User | Node | with_node | The creator of a node is a user\n", "Node | User | with_user | The node was created by a user" ] }, { "cell_type": "markdown", "id": "35c46342", "metadata": {}, "source": [ "### Exercise\n", "\n", "See if you can write a query that will return all the `UpfData` nodes that are a member of a `Group` whose name starts with the string `SSSP`." ] }, { "cell_type": "code", "execution_count": 22, "id": "0992ae26", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:53.988129Z", "iopub.status.busy": "2021-07-06T07:49:53.987441Z", "iopub.status.idle": "2021-07-06T07:49:54.019205Z", "shell.execute_reply": "2021-07-06T07:49:54.019792Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACKADAAQAAAABAAAACAAAAACVhHtSAAAAFklEQVQYGWP8DwQMeAATHjmw1PBQAAAbbgQMvicclwAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "# Visualize what is going on:\n", "generate_query_graph(query.get_json_compatible_queryhelp(), 'query2.png')\n", "Image(filename='query2.png')" ] }, { "cell_type": "markdown", "id": "a2b2419e", "metadata": {}, "source": [ "## Attributes and extras\n", "\n", "In [the section above](query/project), you learned how you to `project` specific properties of a node and gave a list of properties that a node instance possesses.\n", "Since then, we have come across different AiiDA data nodes, such as `StructureData` and `UpfData`.\n", "As AiiDA employs the object-oriented programming paradigm, both `StructureData` and `UpfData` are examples of sub-classes of the `Node` class and therefore inherit its properties.\n", "That means that whatever property a `Node` has, both `StructureData` and `UpfData` will have too.\n", "However, there is a semantic difference between what `StructureData` and `UpfData` represent, and as such they have been explicitly defined as sub-classes to be able to add properties to one that would not make sense for the other.\n", "This would normally create issues for the type of database AiiDA uses, but this is solved through the concept of `attributes`.\n", "These are similar to properties, except that they are specific to the `Node` type that they are attached to.\n", "This allows you to add an `attribute` to a certain node, without having to change the implementation of all the others.\n", "\n", "For example, the `Dict` nodes that are generated as output of `PwCalculation`s may have an attribute named `wfc_cutoff`.\n", "To project for this particular `attribute`, one can use exactly the same syntax as shown in [the section above](query/project) for the regular `Node` properties, and one has to only prepend `attributes.` to the attribute name.\n", "\n", "Demonstration:" ] }, { "cell_type": "code", "execution_count": 23, "id": "67d3b33a", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.029282Z", "iopub.status.busy": "2021-07-06T07:49:54.028540Z", "iopub.status.idle": "2021-07-06T07:49:54.032860Z", "shell.execute_reply": "2021-07-06T07:49:54.033211Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "text/plain": [ "[[816.341503518],\n", " [816.341503518],\n", " [816.341503518],\n", " [816.341503518],\n", " [816.341503518]]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(PwCalculation, tag='pw')\n", "query.append(Dict, with_incoming='pw', project=[\"attributes.wfc_cutoff\"])\n", "query.limit(5)\n", "query.all()" ] }, { "cell_type": "markdown", "id": "accdd4a8", "metadata": {}, "source": [ "Note that if any `Dict` node does not have this attribute, the `QueryBuilder` will return the Python keyword `None`.\n", "Similar to the `attributes`, nodes can also have `extras`, which work in the same way as for `attributes`, except that `extras` are mutable, which means that their value can be changed even after a node instance has been stored.\n", "\n", "If you are not sure which attributes a given node has, you can use the `attributes` `Node` class attribute to simply retrieve them all.\n", "It will return a dictionary with all the attributes the node has.\n", "\n", "Note that a node also has a number of additional methods and attributes.\n", "For instance, you can do `node.attributes_keys()` to get only the attribute keys or `node.get_attribute('wfc_cutoff')` to get the value of a single attribute (these two variants are more efficient if the node has a lot of attributes and you don't need all data).\n", "Similarly, for `extras`, you have `node.extras`, `node.extras_keys()`, and `node.get_attribute('SOME_EXTRA_KEY')`." ] }, { "cell_type": "code", "execution_count": 24, "id": "25e69a07", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.040518Z", "iopub.status.busy": "2021-07-06T07:49:54.039959Z", "iopub.status.idle": "2021-07-06T07:49:54.045343Z", "shell.execute_reply": "2021-07-06T07:49:54.045899Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Attributes dictionary: {'job_id': '462206', 'sealed': True, 'resources': {'num_machines': 1, 'num_mpiprocs_per_machine': 8, 'default_mpiprocs_per_machine': 8}, 'exit_status': 0, 'parser_name': 'quantumespresso.basicpw', 'last_jobinfo': '{\"job_id\": \"462206\", \"wallclock_time_seconds\": 374, \"title\": \"aiida-41078\", \"num_machines\": 1, \"job_state\": \"RUNNING\", \"queue_name\": \"normal\", \"num_mpiprocs\": 8, \"allocated_machines_raw\": \"nid00373\", \"submission_time\": {\"date\": \"2014-10-28T20:07:12.000000\", \"timezone\": null}, \"job_owner\": \"mounet\", \"detailedJobinfo\": \"Detailed jobinfo obtained with command \\'sacct --format=AllocCPUS,Account,AssocID,AveCPU,AvePages,AveRSS,AveVMSize,Cluster,Comment,CPUTime,CPUTimeRAW,DerivedExitCode,Elapsed,Eligible,End,ExitCode,GID,Group,JobID,JobName,MaxRSS,MaxRSSNode,MaxRSSTask,MaxVMSize,MaxVMSizeNode,MaxVMSizeTask,MinCPU,MinCPUNode,MinCPUTask,NCPUS,NNodes,NodeList,NTasks,Priority,Partition,QOSRAW,ReqCPUS,Reserved,ResvCPU,ResvCPURAW,Start,State,Submit,Suspended,SystemCPU,Timelimit,TotalCPU,UID,User,UserCPU --parsable --jobs=462206\\'\\\\nReturn Code: 0\\\\n-------------------------------------------------------------\\\\nstdout:\\\\nAllocCPUS|Account|AssocID|AveCPU|AvePages|AveRSS|AveVMSize|Cluster|Comment|CPUTime|CPUTimeRAW|DerivedExitCode|Elapsed|Eligible|End|ExitCode|GID|Group|JobID|JobName|MaxRSS|MaxRSSNode|MaxRSSTask|MaxVMSize|MaxVMSizeNode|MaxVMSizeTask|MinCPU|MinCPUNode|MinCPUTask|NCPUS|NNodes|NodeList|NTasks|Priority|Partition|QOSRAW|ReqCPUS|Reserved|ResvCPU|ResvCPURAW|Start|State|Submit|Suspended|SystemCPU|Timelimit|TotalCPU|UID|User|UserCPU|\\\\n8|ch3|1134|||||daint||00:50:00|3000|0:0|00:06:15|20:06:59|20:13:27|0:0|31143|ch3|462206|aiida-41078||||||||||8|1|nid00373||36531|normal|1|8|00:00:13|00:01:44|104|20:07:12|COMPLETED|20:06:59|00:00:00|00:00.184|02:00:00|00:01.640|22892|mounet|00:01.456|\\\\n1|ch3|1134|00:00:00|0|8528K|77072K|daint||00:06:15|375||00:06:15|20:07:12|20:13:27|0:0|||462206.batch|batch|8528K|nid00373|0|77072K|nid00373|0|00:00:00|nid00373|0|1|1|nid00373|1||||1|INVALID|INVALID||20:07:12|COMPLETED|20:07:12|00:00:00|00:00.184||00:01.640|||00:01.456|\\\\n\\\\nstderr:\\\\n\\\\n\", \"raw_data\": [\"462206\", \"R\", \"None\", \"daint01\", \"mounet\", \"1\", \"8\", \"nid00373\", \"normal\", \"2:00:00\", \"6:14\", \"2014-10-28T20:07:12\", \"aiida-41078\"], \"annotation\": \"None\", \"requested_wallclock_time_seconds\": 7200}', 'process_label': 'PwCalculation', 'process_state': 'finished', 'retrieve_list': ['aiida.out', './out/aiida.save/data-file.xml', '_scheduler-stdout.txt', '_scheduler-stderr.txt'], 'remote_workdir': '/scratch/daint/mounet/aiida_run/8b/cb/b19a-4cd0-44d2-8eba-f86901618b2e', 'scheduler_state': 'DONE', 'linkname_retrieved': 'retrieved', 'max_wallclock_seconds': 7200, 'scheduler_lastchecktime': '2014-10-28T19:14:21.439271+00:00', 'retrieve_singlefile_list': [], 'custom_scheduler_commands': '#SBATCH --account=ch3'}\n", "Extras dictionary: {'A': 'Pb', 'B': 'Hf'}\n" ] } ], "source": [ "query = QueryBuilder()\n", "query.append(PwCalculation)\n", "node, = query.first()\n", "print('Attributes dictionary:', node.attributes)\n", "print('Extras dictionary:', node.extras)" ] }, { "cell_type": "markdown", "id": "a125177a", "metadata": {}, "source": [ "The chemical element symbol of a pseudopotential represented by a `UpfData` node is stored in the `element` attribute.\n", "\n", "### Exercise\n", "\n", "Using the knowledge on how filtering on `attributes` works, see if you can write a query that will search your database for pseudopotentials for silicon." ] }, { "cell_type": "code", "execution_count": 25, "id": "32265513", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.049531Z", "iopub.status.busy": "2021-07-06T07:49:54.049022Z", "iopub.status.idle": "2021-07-06T07:49:54.050754Z", "shell.execute_reply": "2021-07-06T07:49:54.051118Z" } }, "outputs": [], "source": [ "query = QueryBuilder()" ] }, { "cell_type": "markdown", "id": "835a18bf", "metadata": {}, "source": [ "## Generating a provenance graph" ] }, { "cell_type": "markdown", "id": "70cf001e", "metadata": {}, "source": [ "Previously we have used `verdi graph generate` on the command-line, to generate a graph of the data provenance.\n", "To do this, AiiDA uses some of the queries you have learned about above.\n", "We can also visualise sections of the provenance in a more customisable way, using the `Graph` class.\n", "\n", "For example, lets query for a calculation, then use methods of the `Graph` class to visualise the inputs and outputs of this calculation:" ] }, { "cell_type": "code", "execution_count": 26, "id": "0d5d3c07", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.059281Z", "iopub.status.busy": "2021-07-06T07:49:54.058632Z", "iopub.status.idle": "2021-07-06T07:49:54.178062Z", "shell.execute_reply": "2021-07-06T07:49:54.178428Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "%3\n", "\n", "\n", "\n", "N7\n", "\n", "PwCalculation (7)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N39\n", "\n", "ArrayData (39)\n", "\n", "\n", "\n", "N7->N39\n", "\n", "\n", "CREATE\n", "output_trajectory_array\n", "\n", "\n", "\n", "N42\n", "\n", "StructureData (42)\n", "HfO3Pb\n", "\n", "\n", "\n", "N7->N42\n", "\n", "\n", "CREATE\n", "output_structure\n", "\n", "\n", "\n", "N44\n", "\n", "Dict (44)\n", "\n", "\n", "\n", "N7->N44\n", "\n", "\n", "CREATE\n", "output_parameters\n", "\n", "\n", "\n", "N45\n", "\n", "KpointsData (45)\n", "(Path of 20 kpts)\n", "\n", "\n", "\n", "N7->N45\n", "\n", "\n", "CREATE\n", "output_kpoints\n", "\n", "\n", "\n", "N659\n", "\n", "FolderData (659)\n", "\n", "\n", "\n", "N7->N659\n", "\n", "\n", "CREATE\n", "retrieved\n", "\n", "\n", "\n", "N1511\n", "\n", "RemoteData (1511)\n", "@daint\n", "\n", "\n", "\n", "N7->N1511\n", "\n", "\n", "CREATE\n", "remote_folder\n", "\n", "\n", "\n", "N332\n", "\n", "KpointsData (332)\n", "Kpoints mesh: 6x6x6 (+0.0,0.0,0.0)\n", "\n", "\n", "\n", "N332->N7\n", "\n", "\n", "INPUT_CALC\n", "kpoints\n", "\n", "\n", "\n", "N335\n", "\n", "Dict (335)\n", "\n", "\n", "\n", "N335->N7\n", "\n", "\n", "INPUT_CALC\n", "settings\n", "\n", "\n", "\n", "N631\n", "\n", "Code (631)\n", "pw.x@daint\n", "\n", "\n", "\n", "N631->N7\n", "\n", "\n", "INPUT_CALC\n", "code\n", "\n", "\n", "\n", "N687\n", "\n", "StructureData (687)\n", "HfO3Pb\n", "\n", "\n", "\n", "N687->N7\n", "\n", "\n", "INPUT_CALC\n", "structure\n", "\n", "\n", "\n", "N1179\n", "\n", "UpfData (1179)\n", "Pb\n", "\n", "\n", "\n", "N1179->N7\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Pb\n", "\n", "\n", "\n", "N1492\n", "\n", "UpfData (1492)\n", "O\n", "\n", "\n", "\n", "N1492->N7\n", "\n", "\n", "INPUT_CALC\n", "pseudos__O\n", "\n", "\n", "\n", "N1574\n", "\n", "Dict (1574)\n", "\n", "\n", "\n", "N1574->N7\n", "\n", "\n", "INPUT_CALC\n", "parameters\n", "\n", "\n", "\n", "N1698\n", "\n", "UpfData (1698)\n", "Hf\n", "\n", "\n", "\n", "N1698->N7\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Hf\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(PwCalculation)\n", "node, = query.first()\n", "\n", "from aiida.tools.visualization import Graph\n", "graph = Graph(graph_attr={\"rankdir\": \"LR\"})\n", "\n", "graph.add_incoming(node.uuid, annotate_links=\"both\")\n", "graph.add_outgoing(node.uuid, annotate_links=\"both\")\n", "graph.graphviz" ] }, { "cell_type": "markdown", "id": "dce6091a", "metadata": {}, "source": [ "The `Graph` class also has methods for recursing up or down the provenance tree.\n", "In this example, let's query for a pseudopotential, and visualise which processes it is used in:" ] }, { "cell_type": "code", "execution_count": 27, "id": "b1a42965", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.188054Z", "iopub.status.busy": "2021-07-06T07:49:54.187491Z", "iopub.status.idle": "2021-07-06T07:49:54.284289Z", "shell.execute_reply": "2021-07-06T07:49:54.284831Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "%3\n", "\n", "\n", "\n", "N66\n", "\n", "UpfData (66)\n", "Si\n", "\n", "\n", "\n", "N211\n", "\n", "PwCalculation (211)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N211\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N892\n", "\n", "PwCalculation (892)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N892\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N964\n", "\n", "PwCalculation (964)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N964\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N1322\n", "\n", "PwCalculation (1322)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N1322\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N1359\n", "\n", "PwCalculation (1359)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N1359\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N1669\n", "\n", "PwCalculation (1669)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N1669\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n", "N1765\n", "\n", "PwCalculation (1765)\n", "State: finished\n", "Exit Code: 0\n", "\n", "\n", "\n", "N66->N1765\n", "\n", "\n", "INPUT_CALC\n", "pseudos__Si\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(UpfData, filters={'attributes.element': {'==': 'Si'}})\n", "node, = query.first()\n", "\n", "graph = Graph(graph_attr={\"rankdir\": \"LR\"})\n", "\n", "graph.recurse_descendants(\n", " node.uuid,\n", " annotate_links=\"both\",\n", " depth=1\n", ")\n", "graph.graphviz" ] }, { "cell_type": "markdown", "id": "9142f574", "metadata": {}, "source": [ "For further information on using `Graph` to generate provenance graphs, please see [this section](https://aiida.readthedocs.io/projects/aiida-core/en/latest/howto/visualising_graphs/visualising_graphs.html) in the documentation." ] }, { "cell_type": "markdown", "id": "2c7c77e4", "metadata": {}, "source": [ "## A small high-throughput study\n", "\n", "The following section assumes that a specific dataset is present in your AiiDA database.\n", "If you are not running this script on the virtual machine of the AiiDA tutorial, this script will not produce the desired output.\n", "You can download the virtual machine image from [aiida-tutorials.readthedocs.io](https://aiida-tutorials.readthedocs.io) along with the tutorial text (choose the correct version of the tutorial, depending on which version of AiiDA you want to try).\n", "\n", "````{important}\n", "This section relies on a specific dataset of previously run calculations.\n", "If you have already imported the data set from the \"Organising your data\" module, you should be good to go!\n", "If not, you can use the following command to import the required calculations:\n", "\n", "```{code-block} console\n", "verdi archive import https://object.cscs.ch/v1/AUTH_b1d80408b3d340db9f03d373bbde5c1e/marvel-vms/tutorials/aiida_tutorial_2020_07_perovskites_v0.9.aiida\n", "```\n", "\n", "````" ] }, { "cell_type": "markdown", "id": "b469469b", "metadata": {}, "source": [ "In this part of the tutorial, we will focus on how to systematically retrieve, parse and analyze the results of multiple calculations using AiiDA.\n", "While you may be able to do this on your own, to save time a set of calculations have already been done with AiiDA for you on 57 perovskites, using three different pseudopotential families (LDA, PBE and PBESOL, all from GBRV 1.2).\n", "\n", "These calculations are spin-polarized (without spin-orbit coupling), use a Gaussian smearing and perform a variable-cell relaxation of the full unit cell.\n", "The goal of this part of the tutorial is to have you utilize what you have learnt in the previous sections and “screen” for magnetic and metallic perovskites in a “high-throughput” way.\n", "As you learned previously in the tutorial, AiiDA allows to organize calculations into groups.\n", "Once more check the list of groups in your database by typing:" ] }, { "cell_type": "code", "execution_count": 28, "id": "4f554158", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:54.289625Z", "iopub.status.busy": "2021-07-06T07:49:54.289075Z", "iopub.status.idle": "2021-07-06T07:49:55.713652Z", "shell.execute_reply": "2021-07-06T07:49:55.714027Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b" ] }, { "name": "stdout", "output_type": "stream", "text": [ "/" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b|" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[22m PK Label Type string User\r\n", "---- --------------- ------------- -----------------\r\n", " 1 tutorial_pbesol core aiida@localhost\r\n", " 2 tutorial_lda core aiida@localhost\r\n", " 3 tutorial_pbe core aiida@localhost\r\n", " 4 GBRV_lda core.upf aiida@localhost\r\n", " 5 GBRV_pbe core.upf aiida@localhost\r\n", " 6 GBRV_pbesol core.upf aiida@localhost\r\n", " 7 20210706-063845 core.import aiidateam@epfl.ch\u001b[0m\r\n" ] } ], "source": [ "!verdi group list -a -A" ] }, { "cell_type": "markdown", "id": "f143f137", "metadata": {}, "source": [ "The calculations needed for this task were put into three different groups whose labels start with `'tutorial'` (one for each pseudopotential family).\n", "The main task is to make a plot showing, for all perovskites and for each pseudopotential family, the total magnetization and the $-TS$ contribution from the smearing to the total energy." ] }, { "cell_type": "markdown", "id": "aa761d7b", "metadata": {}, "source": [ "### Start building the query\n", "\n", "First you should instantiate a `QueryBuilder` instance.\n", "To this, you can `append` the groups of interest, which means that you should select only groups that start with the string `tutorial_`.\n", "The query can be executed after this `append` (this will not affect the final results) to check whether 3 groups are retrieved." ] }, { "cell_type": "code", "execution_count": 29, "id": "7a1b0a41", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.722815Z", "iopub.status.busy": "2021-07-06T07:49:55.722176Z", "iopub.status.idle": "2021-07-06T07:49:55.767618Z", "shell.execute_reply": "2021-07-06T07:49:55.768047Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Groups: tutorial_lda, tutorial_pbe, tutorial_pbesol\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/Users/chrisjsewell/Documents/GitHub/aiida-tutorials/.tox/notebooks/lib/python3.9/site-packages/aiida/orm/querybuilder.py:1839: AiidaDeprecationWarning: method is deprecated, use the `queryhelp` property instead\n", " warnings.warn('method is deprecated, use the `queryhelp` property instead', AiidaDeprecationWarning)\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAABjCAYAAAB0Zd4mAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABAKADAAQAAAABAAAAYwAAAAByJMRxAAAnAElEQVR4Ae3dBZg8R/E38AkePFhwd3cPBEsguLsFDe4uIVhwl+CB4C4Bgktwgrv9cHf3+denXnrf3rndvd27vbvdu6nnmZ3ZmZ6enpru6u7qb1Xt1rbtQU1PPQd6DuxIDuwWAqDdkW/ev3TPgZ4DzdF6HvQc6DmwcznQC4Cd++37N+850Byj58Hic+A///lP84c//KH53e9+1/z+97/P4z/96U/NX/7yl+bPf/7zYPvb3/7W2P7xj38Mbf/85z+bf/3rX81///vfRl725djbH+1oRxvajn70ozfHOMYxmmMe85jNsY997KFt9913b45znOM0xz/+8XM73vGONzg+0YlO1Jz4xCfO7SQnOUlzrGMda/GZu8NL2OsAtqACULv86le/an7605/m9otf/KL55S9/mZvzNv81+N/85jfZ0E94whM2e+yxx6BxHfe4x23qxlcapEY3qtGWRq5xd4///e9/D4RCEQzK+Pe//31IkBAshEkteOpjwkh5Canf/va3jWcp80lPetLmZCc7WXOKU5yiOfnJT56b4z333LM51alO1Zz61KfOjcDpaXM50AuADeC3RvH9738/tx/84AeNzf8f/ehHzU9+8pPmZz/7WaO3LBX/NKc5TTaSbgPRaPSoGv9uu+22ASXd2CyNRsqopQi5WsA5/vGPf5xCEE8ICzw5/elPn9sZznCG5oxnPGNT9vjT03w50AuANfJT7/iNb3yj+da3vtV8+9vfbr7zne8M9gTA6U53ukHlLRXYvvR4/fB4mPFGHEY9RkWE5K5duwaCkwAlPE17zna2szVnPetZB/uzn/3szTnPec4cVQzn2P+bhgO9AFiFS4a8X/nKV3L76le/2nzta1/LTSVV8erKWCqnXqyn+XOAAOgK2+9+97v5bUxrzn3ucw+2853vfM0FLnCBhi6ip/Ec6AVAxRuKtqOOOqr53Oc+13zxi19svvCFL2SF08tc8IIXzAZfKtlZznKWnONWt/eHW8gBU4winO0Ja9/RVIsg8P1sF7vYxXJKsYVFXahH71gBQCuugnz605/O7TOf+UwOPS9ykYtkRSmV5jznOU+vzV6oKjtbYUwlijC3/9SnPtVQehIEtktc4hLNJS95ydQ/zJbz9ki9YwSA3v1jH/vYYPvsZz/bnOMc58hKcPGLXzz3enea6562NwdM3wh8m3rwiU98InU2l73sZZvLXOYyjf2Zz3zm7c2E/73dthUAFHEf+chHmg984AO5UdKR+D6w7VKXulRzghOcYEd85P4lJ3MANuJLX/pSc+SRR2YH8fGPf7yhlLzCFa7QXPGKV2yudKUrpYCYnMtyXt02AsAH+/znP9+8613vao444oicy+vZfUCboR5wS089B6bhAGXjBz/4wew83v/+9+cUYZ999mmuetWrZn2Cw9gOtNQCAPBEgz/88MObd77znanxvdrVrtZc/epXz2EcxFpPPQfmwQFKRfXs3e9+d+qMLn3pS2c9u/a1r53LvfN4xlbksXQCAHjkrW99a/OWt7wlh/iG89e5znVSMltn76nnwEZzQMfzvve9LzufN7/5zQleUgevd73rNec///k3+vFzzX8pBAA02Zve9Kbm1a9+dWruDcUwfL/99kuU3Fw50mfWc2AGDoBO0xnokHRMEJs3uclNmpvd7Ga5bDxDVluSdGEFAAAOhh522GHNhz70oUajv+lNb9pc4xrX6JfltqSq9A+dhgNWFV7zmtdkZwXKfYtb3CI3CNBFpIUTAMA3L3nJS5pXvepVuR5/29vetjHPYuzSU8+BZeEApbRVhVe+8pXNa1/72lx5Upevda1rpZXlorzHQggAuHrD++c85zlpPHKrW92quc1tbtMjthallvTlWBcHGEW94Q1vaF760pcmWnH//fdvDjjggIVYWtxSAfDDH/6wedaznpU9vnX5u971rg0tfk89B7YrB9guPPvZz24OPfTQ5nKXu1xzj3vcI5cVt+p9t8QjENCFudGFLnShRN7B37/jHe/oG/9W1YL+uZvGATYkT3va09IM2nL1Pe95zwb83DQBIGnTKeYqm0ahLW333XffNhQi7ROf+MQ24Lmb9uz+QfPjQFTUNpbC2nD60Qbicn4ZryOnsO1ow5y4DYvBVvmWid7+9re3e+21Vxvw4/aQQw5pvctmEcjjhlNoRttAULXh6KENBV8bGv4Nf2b/gI3hwF//+tf29re/ffu9732vDeVs++QnP3ljHjRjrqFsawPt2YZ/hjamkm14L5oxh61P/slPfrKN1a5BOwmjpQ0v1IZOAcx3gCOue93rpiYfHp8mtHf9tOkDvbk8MBp/ot8osc50pjOltyLmtotAymHjkyEEVOJEWP0tE4Grg7FTiL/iFa9oznWuczVve9vbNvQVNkQA8Al3v/vdr4HFt2n4d77znZe64Y+bnwGChJhe90cqeXhOOZ6UqefOQiV92c9yb0lLecUJCoUtgrzkzqymSXySbj3PL88p/GHSXUhZiiMWdv/cqGlEy0hgxozYnve85zUPfvCDU0nIzmVDKJg5VwrplXP8O9zhDm04aZhr3luRWZiMtgFAyilMKC3bO93pTm1UrPZFL3pRDjkf8YhHtOG3rr35zW/eGrI99KEPzbTxEdsb3OAG7c9//vM2/AG2l7/85dtoPPkKz3/+89uorG183DZcYLV3u9vd2tOe9rTtwx72sDYcaLbhxaYNZNnI1w0Iahv26zn8DiOn9pvf/GamC996baAj21hnbqMBtI95zGNyLmxYGai09sY3vnEbIKrM3zzZO4RwzrI/6lGPWnXebOivbAHKGpQLDwK3kf8Drdne+973bm94wxvme3refe9733bc88OGow1ruzZWfdqw0mzD8CbzCa14GwCalr4obPeTx2Gmndee8pSntAG1TX45FyPJNjqWvPbHP/4xeZ9/4uf1r399G6OU/Cbl3DLu1akQBO0pT3nKNhSGqeOY53vMTQdgTkjBF66YWnP+7UKhoW0D791Gz9WGc5BWo/OuFDchkbOCxvpu6jYe/ehHt9E75qubgxIYYauQ9z71qU9twyPugC0XvehFc65KH0JoyMuHDtv0vC98FQzSloOwUMtGGPYQeUoawoPSy7M8A4XjizYsH9unP/3pbfjTywbpfyDU2vvf//7tRz/60RRIGg0B59lh8Zb3jvvxTaUrz5aOErDMtQ8++OAUlM4/8pGPbMNFVxvOVkY+X/nCmq6NKaLk7YEHHtiG4Vb+D99/+ZwiaJ7whCe04Qw008lPGQi58PqTwtj/WFXK6+FYNPd+CA/X6AS2A8Wouo3pc3YU4zqHtbznXASABqBy+1ibqcFcywvPcs+vf/3rrESxRJO3BXijvcpVrpLHBIJKrictZHXjsY99bPnbahQqYfgUbAPkNCQACAbKKiQPDbRQwJ/zvtJAynk9AMVbIddVeg3acwiIQgRXzCHz741udKNsNOXaLW95y/a85z1vjlqMXPQuD3rQg8rlkftAZrbhLCWF2agEenMjHhTD1zYw8Skg/O8+Xy9vlFKIQFV+oymafMdFADzjGc8YCIAAjOW1sAvJW61EGAXgc5eMrORDUG8nwpdwUdcGUG4uo4F16QD4fo+P3kTPk5Z5D3jAA7aVzT1/9ua8YMlsE7j2Zo+AGH1wRFlMjl3n2rqmMlfmlmoWYuGIYng/dNvXv/71IeMnXmui8ab33KGE8YebK+VF/CCUcvpPJxMNP+0s2Foo9+Mf/3iXxhIFYDSksXN4XnRiGJ/P9Fx289x8o+7zS7nKwygUufzuni/Xx+3FReC6jXfmLok/gOijthPFVDKdl1Ckc3zKm9F6aM0C4Mtf/nITw9jUBnOtpKFsR9L4vR8l5u1ud7sGTHkUcfPN+CPmvIPLKj6K+X3uNaBpiDMKxBlpTRoUe/RakRY9wkD5VVcGz+4q6EpeTFY5uaiJWeskUhbP5bp7FEXvnc+D5gR9ffnLXz4qWZ5TLvWH16ZCylv45Nw0vJLGShPXbl1iQYq4Z99uJPDLC17wgiZGR7naQVm4VlqTALBMwV3S4x73uOZJT3pSRqJZawEW/b473vGOTSjtmgAu5VJm8RlYtPV6xkJGB/wOxtw6T3FAGUPttBFXuUXNYeykJ9e7141J44opR94HFcmLEdQYeuELX5hYcjBp/hDucpe7pMcjjY6jCiONUESmfXreED+erZdHRid1OSHQ2LPDo3N5Do7dHW3kjdUPf4lGPPz2j6LQN+SoQ8W01EsYFuo+H5/w7z3veU8mERwklKVNKA5zhEODT0DhozTcgbMXKVT4htfea5RQ5vePUFHu7UoMizi1ZUMT+qf8zjO/a0jRmYiiKZZcBtrfmW5essT0GbGunHPJYGzuaZ8plqIR5n/KN5puBNl4zWteM1FdD3zgA1NpGD1dXqMsc695NM33la985XbvvfdOJRwdgPyBpZyPYW1LGVaIYtU99A70EPKgf7AiUcAilHm+C6Wg1QDa+Ggc7ete97pUHMZwOVFm8lSWwGbkM2PtPO8pyrzyzFF7SigIzlEU07/Mr/ApGnEbo4CRz3d/wGHbGAm0FKf4AgFXCO+8XwjALJt9CKm26ACspliZoRws+oByb9kfdNBBufJR/m/nPV2I7x2+C2fWC8ykBKTk84Frbet2ZqwlNhrnmJumwAsno7lUFyOfia+NP+7VYLtEc45imDy4VJSAKvgo3lq205gL+eA0+F2yGhDz4amXXyncZoHyUqxRLHaFRfTwbQC+UhBSeBKIb3zjG3OloVvG+r9nS1+/W7luWRHVfCoCQKNXliL8yj1lT2NulWW7rACU91ptH6PVfO9R/Bx379QCgJaYpn87rO2PY0b3/K1vfetc5iuNLawXc92ZIJgnWUXR4y3DCor1+UACDgmBQKu1MS9tw6gr2YJfVg0CDDZPNqX9gQ6orMqMypxQMZJSzp1IYNpGiNMK9qkEgGGfYVe9BrwTmKs3te5vjdow1dLLvBu/pTtLgiq2Zb7QEyw8a5W5XnLU48Q8PIfklhQBjp773OeOHAGt9eWMpiwf4tPlA1RVpl3d/EIn0sYqR/f0jvqvPtmmGQms6g+Awu/hD394ardrxU58iB1DMdTcsOVNyjBa80LcTVO2rYdoxi1hiiyMaMQp70Q52kiKVpYrBUVROu9nxdRnkCVNeG9TMmDHigPKa27IDjzwwMmRpSeJxtBWpwIpQmhNStZf20QOALZAAALaUEDqbSkOQYmdM/SLgKUDkJGeGOrO6KWnncMBehp6kFilm/jSY6cAlC+GveZyPS0WB2jEQ+QnArAuGfQf5RnILNuAQtCLtQBYFBPeUr5+vzEcYIdiSjZJHzJ2rGl93zp0Qb6tGGP0J7aMA2VoD41YKCR+OqAU7kwItGIZ53o9VOZefTXUX8nTHj4hqmd9qqmt8FzoXh9K3P/ZMg7sueeeTRhQJYjNNHYUjRQAkGjAIWHtNeqe/twCcgAMe/fdd09IdqwJN2GNuaKUgS1IM23AmsArJMKR4IBypB9ghirCEnrxi1+cwJ6wFMxAmVy4AQsBF+kUwHcLKIjJ973uda8Vz+tPbD0HxCfwrcD1R9FIARDr/YkSg8/uaXE5oHGGYU8Gr4RWRGCxYVzTaNhdAovVUI0gQpeQIwX25lCEEIGcVELx6S30HtCEHFSEdV8+Q29y4QtfOFGJBEJB57EDgEvvaTE5ALHr28WqwIoCroiWyVOvgBwMRnpabA7Q9LLH0BPzLosM/WtM/ag3KFMHw3uCA+yYAACxpcEHLwUXthoRyMJ04CqfANY0Ye3YhGu3fF6J1cDJZU+Ly4HQ5Q06ifvc5z5DBV0hAAKVlpjsRXH1NFTa/s8QByzz6L1tRm3TfrMiAFgBWoJkVFLsDuoHEAC1FaHKA38f5r0Z4GI1A6I6r/54aznASCug401XAKyYAohkUoxItrbI/dNn4YAY9tMKgJIvAQLbUVsGmjoUPUBJV/bhXCRdVRkFMFjiyrqn5eAA/Q7dDyvMmoYEgOF/GLQMfL7VCfvjxeEAq0JUTF67JQsswNB8T6NmkYcAjdjI+86meSwMH/KQh6RFmcrB5NmIYpS1Y5lHSm8UwLIRsZQM7H8e9z+LyQGjPsrhFcK9XoGM8EVpzVaf648XiwOAQMVC0bo/b0A1sStgicdug+sobthZAloPhtkH4Q2tcFri8S7DtRYrwqi2CTBiwQd2O8rakWcfMFxGYaweC9aAh5qIiFsXoz9eQA5wnV68NpXiDUGBwzddw8sKCd/T9uWAeb/5PTgtisqQkWpWc55hdcDIACQX1LiQ1QAOUdYLYS759fuN4QCIePjtHFLwDykBDSnHeZHZmCL1uW4FB+AFajI8XK3xS8/Bhq0IjpJHrSgs5/r94nGARylu/Goa0gEQADvV4KdmSn/cc2A7coAAoB+y/FtoSADoGWqLq5Ko3y8eBwzbw7320Mfc7FLCH6hQpgZclRWCFyhuu7rlpIzcNaOTVB3TKBBLeV7ZT5uui3Gp7wOi2m6ORAt/TNVM0+qp2pAAiIAUK4YI5eZ+v1gc0PhFwAkl4KYXLNyM5ZShrAr4D1rMh58lZGjEiCOQ5arLCTdgqhHOZWYqM3TiNI4v63R6uXAt1uy9994ZnQq+JeIi5KpH9/n1fRyrhqIzV0NmKuQSJDb818ZrGhIAoQ2eWTrXmS3zscq8SBSORxKrP65MoLcRXSfx++PSbNT5iDqUDR7PuN+GP7Bx5hnmx0OPrcspRiTY8KwEujwNxqFOF1GWmogOlJBnjZ/LcnkYfXCgWVN9HwHG8/LLXvay9Dhcp1v2Y6MbS7w1DQkAVmS1W+s64XY+ntVCzrAWda3i1sOjOi8IPVh7PWpNerXybJBdvvcp5QqVa/bluFwr+3r+V87V+3H31WkYFcEPFNJxFOvD2vLQ9W4516IwlH+tnB5XxpLOtIRwEiSUdaRRCKCU1Q+AGEE3ayr3lXMAUnpK920n0ra18ZqGBIAgC+ZHEEM7hboWcqwgRZiFsS/DWOAYjY0rba6YDXfDEUcumUrHBTUy94WrF7CDq+pwJprnJ/2MsrCLmHqNcunF4Oy7lnksvLiC9jEh8sxhYfYFCZEWVt9QO9b4B492HhDEPeEfoIk4fon/HyT438E0ln2ESIETu63bgEqedAN1Ocv5sg8XaBnYw7IzATiOf96nCBigI72Y5cgulXTqMF0DewbEtbiRh5HLgQcemOfqn3Jffc77rSYs6/TLcAzGLWDMEIU0HSKADjHqdhLV4ae8N+cZPOxEBU42RAVtA0GVzjZ44RG8kxvuQw89NEE2gnryjMuBCjfgvPgKYlkCV07ipSCqxUGHfEq4r/hI7Yc//OG8FfjH/wgf3QL6CMp55JFH5jngnqiomYc0QpPJA9iHi3AUDSLBQZy88MIrHc/GxUNxJvrfT1gLZv71ufpYMFD3e/+aijfj6H3z+uGHH578q8spvUClAEXCgfFkFI0+s5nEP+UMNGOmC0O1NkZHI/0NlnQhTNKTFb+BgtWKvehb8bnIdbsQZHwYRsTdzLPcl3/+9wPoBAy1XYg/TwAx36cmQ8Uh4nI5zEKHzm33PwRAmL8OXjOGflmJubZGHCyqVIibcD7pC4nVFyayibKL4eYg3h7f/uLvrUa8LcfyTPrP56abI0eNSSMrAkADD83tUBxCDUIaDQIRUP4Xx62i84oPgAh0XnsJABRLvW0E8MjjWX5iHp0RfXlLHke1AJCmW04CgHAkSGPkNMiGIFsL/wYZdA6gHD0rQppnGcRWiBFZPpuA9+wiIDu35l/BUtUJSEoCfdmJaziCt0v/fwIZtQdFhc6Y5AxEzJt2CtVDWqGzRD6KypPOFNjA13PtmifmlBGEIhVG4Ysv4+3V11c7nmRhV8pkb+lm1Py5pOk+BzKv2OuLHGSILcwZRRzbgPDj371l1f8iFEUFSquyVRN3EtTlpIwyTxfVqAztodTWwr/OYwZ/afJtiGIyhGF6TDLNMBRmymyKMo5CQOV7msLg3zKHvjMdMv3hA6JLQzoAF1U0TiLcsJOJfT3NMWcYk6wjzTfFzSM0zLmFuCoUYavTOUf5P2o/ycKubjSj7p32HN1OhAXP+XgJ5FpDeafNh6UoxRodSBEu095bp9tvv/3SmIieJOJM5KW18q/Od9QxxZ8lxIhR0MSoIIWCxu+5lH3jyEpB+NJLPdAyN37vF9PG1LXoyLq0QgBIwMsMRaAbdwLVFnJ6CETZx5USC7mYew+xoYBcgFMiFHbDMcdee+2VeHgjKOeMoIwMKKwmUVlLry3slAdRIlISjrLM04Oistero6K4cr70cGIRkv7W6zUEAmEcrWbZRwGp8cf0ZGQWpRxGHKiUr+ydV0aOSKwYhM4pFXbT8g/ugTCbloziYsqSS5SUpLATiA+E61//+mOzYepM+bvsZEWJMpmPz5HUnROU/9F7ZaAH89LtTl0LufK+oTFOhVH5b08HICadWGzcb4vPFw00k5hXx9A75+ti+VG4rUbjLOyE2jJvj5gMKyzzzEkpD+ODpr6G23YKNf/vfve7t+a/0cOnbsG8nVdYOgTXbbE0N9Z6bxrLvlhPT0vD7rsJWWZ+7RkCqtCl1OU0H2elGOvuqbuI1Y5MGysPyatp+EdRSlcQAqX7+BX/KfzoG+hVUAz9W8+KEV0b6/wr0tcnKHqLDqg+v2zHrP9iFDu22CuUgHVKDSAk80ABVl/bbscqSfRsQ68VPcSKKDNFCRhLbyMrIUUbd8yFVFQKp1GbKECUi54bkYHLLYO9Z8yDrBqE/8B8l1jabGM5s4358VB0n/Ic5S8CrZzr7mPe3kY04O7pufzv8q+bKYUoQTMNSUfxNyv5FoSvlYxlJsrtGPEMlL+j3mWFEjCk94CEn+YYksfZcV5FB4mX/KBYyBneh+Y3HWd4pa6rLMNqShXosVFEUVcr60wFxvGOosm1URZ28h73jFHPHXfO9IE+g17Her2N/z8WfaOmJ3XZx+UZo5bm4IMPToitdeV56So8r8u/bhk8i8n6NCTdtGlLfiGos75TTlL+LStRdNLj8e848ZuOkgr1OdJQXEDxAXcCWd8m/YVaDgXd0CtzsGFoaBktgDVD1xb5j6G3JcmoCG0AYlrht+tRyqxl1wubIsq3y6NZ81q09ByiwC6UacOilW+a8nDywgFMQMVXTT5xClDu5vklJG8bASXKqaXdC9u9WoRjoBTpumSobv5tGzUHBSiZZt7fzbf7X/6ljPIsz+rmD+BTTxMEM51E3qsm049pdDzTpqvznsdx9/3G5dnly7h0q52vA55KW3hvWgK4ZL/oJDhqrG5MjKBcv8NUAsANKiUkUUBT6/uX5phCCDAkhnQT53YaERBPwENnerd5xOAzXzVnE++vhHMCNIIUHJV/iQEITWdO791mIS6ioAtXo2nTrZbPrNfL+026bxRfJqWn34gpbfIrYNGtHh9aUEhxOp+aCu8JWToUvSq3eYtKgGPqwDQ9f3mHmWqMCuqjxNyi3L9U+7DzXlUAeKGY384sANzXjcHn3CwUa88pveueGuouADiZTTf/OgagSjyrAAg7gnafffZZtYh1Ooq1WMJb9R4JZkk7KsP6/UZdL+e6fCnnR+1DF5Iw6TC0agn7MAVOFKWAq4bONdW8d/6Zz3xmyw/jIhJeWWGZVUCNxAFERRpJFEjAETEXzvBQ1sGXibqWaqXs1rXjo5a/uZ5f/tTny7lx+3H5l/Sr5cUrM/RlnQ+eF0u4+rw8GfYUJN1ERU8pQGcv73K/S+PKV9K5zrd8YOg7Oa38O0vacnfBMJR9/X4lTfdbOd/lS0nb3cdUal1WgqFHmerdu8/d6P9Mlxk+AWpNwjaMKsdMAkAG7L/ZqoOa0pKqtMtKodvIIChAOCwARUQqRHsOcOJ9XbMaggBdRsXSK/eN20+yYiv3aDSQmDWNslRzHUpxXAzA9773vbl6sf/++zfRy40tcy1coBitOkDLdamkU8GYTnMtraLZi0KEf9HbpwkzfnGq0U0rTxp2UGRgKSsg4gto7HgLbQlkBGUoJJ286/eb9K265R33H2rTCs5arQStQIwTkuOeuZHngapYdQr9BZjFynNWGq5tU95tySysqxIWSkrDmC8jQUfxsW8pTjDNerlOZWVSG0Yq+dGZ6KJxsfRWe39wUrH3ug283KdiQblBq9UEs9+1X3d9XAxAy5QaH7NXy5meO67MbOWZESPQ4NAjZEzAPFH9lHR8FMQQOFGS4gCA9BJQ/BaA11p2ilWjbGTdtBq6xo0HBK36o4zMr0FyA8yUsQXcR3BxJMJmoCALJ32rqqgTDwVA9Z5GEbwD4bWGo0yEXwBmEjVY4h10eQ9BaaQ1SkhOfPAGXIQQtQTLhoLZ+qg6MtVjo+Kti5iHWiZkollrpNeV6QbdTJseTBkoAQMim0s+VjkogJgAoxAEOU8sxTjkkEMSSUczH8KvjR4k0WTmnjSuEGYoGkQi4cp90+537drVBkQ3l+ocj6Nu/kBJ+I7C3Va+GzPWqNyDLCi9JpV5kHDKA8uItRVduNxqI+xU3h0+ArMMlFGoTit+Ad7Xmnbz6ai4mRYi0vvUVL/fuG8lfZcvdR7d42i867IShF6kOI1RVjfrTfkfgjTNtSE9p9XFTCrYRCDQNBKEVDQnBBYihQIhlsPqae7d6jSw/iWwpR7V0LVQbf3HFkAPRvLHUtDYWHrl3ln39CocewDs6FHXQwAgetQwB85sYMHnXeZZgD8lLaOnLunBjFIQfk/SY0z6Vt18J/03yrGhtVgJGqGYjrg34NeTHjX3a3p9Ohi6N3WGEdp6aU1TgO5DfbjQkKZy0DDQUM/wbdGJKS6rsOjhEyE3rryUR0iFnSWW3rj8uufNfQ3/Oavkj249FHj6xlZCdREGG1HmuoyE42pUlI1dIVsUnKvdP+23Wi2fcp1QnNVKMEa42eh5faIL2SwyZdGuwi9F6l7wcB6NX/nnIgAKI/SURgPcL3GVxfJMDLpFoTKfpDxBYLrmryQqpRpGB94hr5U0/tADsCfXO4+LpSed/CmZRtFqVmwaKQXZpIrVzZ9gKisxpbzmrebQYZTTMEemc5hU5lJWrrAPOOCAVZ3CslTUm0sPNk0JaA6qceATIlRRnRakGvSYI9NCRfnnP76VdynX6/eb9K26fCn3T9qvxUoQP+mMNtNKMJZ3Uwnt2XwUhrFX+lmc9G4zXZs0P1jPNfNllmBgs6HoWWFos56813KvNenaUg3KTIy7GHq2LOC4yGLJF42wDQmb8ffMLVm1AYQw3EHWikMrnvNZa8eAJIjnmMCdD2Lw5cnqZxorNvxiqTaKuvlzDVZiAIbTigS2xIdvo6fMeIGOrQu7b1yZ6+dwj8VicDULOFZ0oNL4AikXgrNlHagsodTLPXdbgGPdtGDWeEeHwFsRi8po9OkNyZwW/+hbUP1+INjjvlWXL/U7jTteq5VgCKHk0bh853meriJGnIk7AE3eKKLh3lDiI08DiqFeotlCS72hz5s1c4KhUBfqqdGroF2iiInlz+7pif/dUz9rVGI+67gf2wiapsyUpNNQV9mLb4V3ZV/y6aallATA0UHMSjX/us+Rl3ccZXVZzrmO5LMWK0H2HzGqyTw26kdjp1gNx7LtYYcdNtL34TyfveECoBSW9GcjrweAqJq2spX7d8Ke1A9QS46Y6sq+E959Hu+oZ9d4xm3Qf2slSLuYwrTh/GWtWUy8LzwXJypT+zD62ayOcig68ExzhzUmDkZmPHnrr+F1NTXf1mZ7+n8cMH+mj4ghda6z93zZeg5YSTniiCPSMxRcRVnZWG/JgM0Cupum1XQ4VtIohOsVqPU+Y7X7N10AlAJBZYWRS7qpsmoQa8lpX16u9/ueA9uVA4S8lRqrEJafIU5D37QlrzvXVYBZ3iCMitIvHCQT1BffcFCFcM0x/J0lqz5tz4GF50CM/xNHAvKs0UOaClhitLdVjR/TtmwE0P1iGGSJKNxXpVNNy26Ws8A0e+o5sKwcEOEp5vTZsYFU86ocVoY5xVuEd1oYAVAzQ6jp0IAmSiy0velCOoJx5EihTtcf9xxYRA7ACpjbW8NnZAWVKQLxKLfcW13+hRQANVMYZjDcwEwGJ5QkRgcMO3rqObAoHAhMRMK51VMwXcN6nRYQ1rTmylvxLgsvAGqmgEDybQ8txxyZILCBSPbUc2CzOcAmAOSaf4yjjjqq2XfffZtww51Q+BLbYbPLNOvzlkoA1C8HcozxNss0mC+Crz1YbU89B+bNAUt1AdRJXRV9FW2+FSydEFv8ScZM8y7LvPJbWgFQM4AkDh/uaUgjKk9E6U3POow2xO5bxg9Tv19/vHUc4EKdpt4mPDyLV8N6m1Dq88IEbNUbbgsBUDOPlGYS62MRBowoLC8y3WSk5KOVGAD1ff1xzwErUcKoqT+Mj9QfgCyBYrlq08tvN9DathMA3WoMU8ASjwS3JxDgDugNxKMjEIqpavfe/v/25gBrQ1NJw3qbhq+Bqxd8BhhBrtc/w6JzcNsLgO4HYPILjuxj25ikUigaJRAGTJr5q4sAIN1b+/9LzAEmw8KR+/Zhl5J7IDTBQvl5MDq08Ue4k2jHCYBRHzcswwaVgjaXfbuhn8rB45G9LSy05muLPaow/bl1c0CIdnN3S8g2dvSg55xo8FNRhL2Q5JuJu1/3i21ABr0AGMNUQqFUHnsVCqqLh2BKRhssgkoF1rwsyz5jXnfpTjOk4ZhEL+7bANyYv9tH9OOc5mngRYj7VkZ6PQ1zoBcAw/yY+M/0gZttlczm2KYSmjIQBAQEoWDuGKad6QWH99ll1xZPZMwGXeStiSDW0LmfD4ei2ZPbO4ev+E0zTyCXPdfkPU3HgV4ATMenialojy1FGmaqnPwh7tq1KyupCsxdFoHAJZZKS+nIF559OYZdmDWS7cRCLfBFrsvBvbkUw7fwJpzGMfZlwz/p8Iwg5RTUVoSs6ZgIxz2tjwO9AFgf/6a6mwJKj2UKwQqsW+HNWTUGgoQSSg9mc0wXsccee6Tf+rI32rCUCRptIzgYmmwW5DQ86zQ06FZYbPUxSCyBx2egvY1fSKAZ76jh87tI4BGGnJYWIVgEongMBGaviJ2qeq0rUS8A1sW++d7MKWZpJBoKo5LSiEqDstegSuMrDVCjMvclBPSM9UZYcA5ab9KW/+bTGnXZOyaMTHkIr3qDs2CgRejUAshx+AbMrQgqe5uGrMEX4eZcPyWab91Za269AFgr5xbwPo1zVKPlcdc1DbtspbFr6EUQ1EKBdnyUMIGq7JGVC/jx11ikXgCskXH9bT0HtgMHtswj0HZgXv8OPQeWnQO9AFj2L9iXv+fAOjjwf1yA0uUYoe7CAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = QueryBuilder()\n", "query.append(\n", " Group,\n", " filters={\n", " 'label': {'like': 'tutorial_%'}\n", " },\n", " project='label',\n", " tag='group'\n", ")\n", "# Visualize:\n", "print(\"Groups:\", ', '.join([g for g, in query.all()]))\n", "generate_query_graph(query.get_json_compatible_queryhelp(), 'query3.png')\n", "Image(filename='query3.png')" ] }, { "cell_type": "markdown", "id": "2a0903aa", "metadata": {}, "source": [ ":::{important}\n", "\n", "Most of the code cells below are incomplete, and need to be completed as an exercise.\n", "Look for the comments for more instructions.\n", "\n", ":::\n", "\n", "### Append the calculations that are members of each group\n", "\n", "Try to complete the incomplete lines below:" ] }, { "cell_type": "code", "execution_count": 30, "id": "3053374b", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.771659Z", "iopub.status.busy": "2021-07-06T07:49:55.771064Z", "iopub.status.idle": "2021-07-06T07:49:55.774142Z", "shell.execute_reply": "2021-07-06T07:49:55.774730Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "SyntaxError", "evalue": "expression cannot contain assignment, perhaps you meant \"==\"? (3468577197.py, line 2)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/3468577197.py\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m query.append(PwCalculation, tag='calculation', with_group=) # Complete the function call with the correct relationship-tag!\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m expression cannot contain assignment, perhaps you meant \"==\"?\n" ] } ], "source": [ "# Retrieve every PwCalculation that is a member of the specified groups:\n", "query.append(PwCalculation, tag='calculation', with_group=) # Complete the function call with the correct relationship-tag!\n", "# Visualize:\n", "generate_query_graph(query.get_json_compatible_queryhelp(), 'query4.png')\n", "Image(filename='query4.png')" ] }, { "cell_type": "markdown", "id": "321fd1a2", "metadata": {}, "source": [ "### Append the structures that are inputs to the calculation\n", "\n", "We want to furthermore retrieve the crystal structures used as inputs for the calculations.\n", "This can be done by an `append` `StructureData`, and defining the relationship with the calculations with an appropriate relationship keyword, in this case `with_outgoing`.\n", "\n", "For simplicity the formulas have been added in the `extras` of each crystal structure node under the key `formula`.\n", "(The function that does this is called `store_formula_in_extra` and can be found in the first cell of this notebook.)\n", "\n", "Try to finish the code block below to project the formula, stored in the `extras` under the key `formula`." ] }, { "cell_type": "code", "execution_count": 31, "id": "25922462", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.778755Z", "iopub.status.busy": "2021-07-06T07:49:55.778074Z", "iopub.status.idle": "2021-07-06T07:49:55.781198Z", "shell.execute_reply": "2021-07-06T07:49:55.781593Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "SyntaxError", "evalue": "expression cannot contain assignment, perhaps you meant \"==\"? (602642116.py, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/602642116.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m query.append(StructureData, project=, tag='structure', with_outgoing=) # Complete the function call with the correct relationship-tag!\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m expression cannot contain assignment, perhaps you meant \"==\"?\n" ] } ], "source": [ "query.append(StructureData, project=, tag='structure', with_outgoing=) # Complete the function call with the correct relationship-tag!\n", "# Visualize:\n", "generate_query_graph(query.get_json_compatible_queryhelp(), 'query5.png')\n", "Image(filename='query5.png')" ] }, { "cell_type": "markdown", "id": "c47880f0", "metadata": {}, "source": [ "### Append the output of the calculation\n", "\n", "Every successful `PwCalculation` outputs a `Dict` node that stores the parsed results as key/value-pairs.\n", "You can find these pairs among the attributes of the `Dict` node.\n", "To facilitate querying, the parser takes care of always storing the values in the same units.\n", "For convenience, the units are also added as key/value-pairs (with the same key name, but with `_units` appended).\n", "Extend the query so that also the output `Dict` of each calculation is returned.\n", "Project only the attributes relevant to your analysis.\n", "\n", "In particular, project (in this order):\n", "\n", "* The smearing contribution;\n", "* The units of the smearing contribution;\n", "* The magnetization; and\n", "* The units of the magnetization.\n", "\n", "(To know the projection keys, you can try to load one `CalcJobNode` from one of the groups, get its output `Dict` and inspect its `attributes` as discussed before, to see the key/value-pairs that have been parsed.)" ] }, { "cell_type": "code", "execution_count": 32, "id": "0f9dae9d", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.785660Z", "iopub.status.busy": "2021-07-06T07:49:55.785029Z", "iopub.status.idle": "2021-07-06T07:49:55.787762Z", "shell.execute_reply": "2021-07-06T07:49:55.788147Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "SyntaxError", "evalue": "expression cannot contain assignment, perhaps you meant \"==\"? (2178412746.py, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/2178412746.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m query.append(Dict, tag='results', project=['attributes.energy_smearing', ...], with_incoming=) # Complete the function call with the correct relationship-tag!\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m expression cannot contain assignment, perhaps you meant \"==\"?\n" ] } ], "source": [ "query.append(Dict, tag='results', project=['attributes.energy_smearing', ...], with_incoming=) # Complete the function call with the correct relationship-tag!\n", "# Visualize:\n", "generate_query_graph(query.get_json_compatible_queryhelp(), 'query6.png')\n", "Image(filename='query6.png')" ] }, { "cell_type": "markdown", "id": "c87fdb57", "metadata": {}, "source": [ "### Print the query results\n", "\n", "You can print the results to see if everything worked as expected." ] }, { "cell_type": "code", "execution_count": 33, "id": "1c851ed8", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.792896Z", "iopub.status.busy": "2021-07-06T07:49:55.792287Z", "iopub.status.idle": "2021-07-06T07:49:55.795148Z", "shell.execute_reply": "2021-07-06T07:49:55.795512Z" }, "tags": [ "hide-output" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tutorial_lda\n", "tutorial_pbe\n", "tutorial_pbesol\n" ] } ], "source": [ "results = query.all()\n", "for item in results:\n", " print(', '.join(map(str, item)))" ] }, { "cell_type": "markdown", "id": "387d996d", "metadata": {}, "source": [ "### Plot the results\n", "\n", "Getting a long list is not always helpful, and a graph can be much more clear and useful.\n", "To help you, we have already prepared a function that visualizes the results of the query.\n", "Run the following cell and you should get a graph with the results of your queries." ] }, { "cell_type": "code", "execution_count": 34, "id": "917912a5", "metadata": { "execution": { "iopub.execute_input": "2021-07-06T07:49:55.804224Z", "iopub.status.busy": "2021-07-06T07:49:55.798685Z", "iopub.status.idle": "2021-07-06T07:49:55.817462Z", "shell.execute_reply": "2021-07-06T07:49:55.817927Z" }, "tags": [ "raises-exception", "remove-output" ] }, "outputs": [ { "ename": "ValueError", "evalue": "not enough values to unpack (expected 6, got 1)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/1125434548.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplot_results\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresults\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/var/folders/t2/xbl15_3n4tsb1vr_ccmmtmbr0000gn/T/ipykernel_79735/3550446730.py\u001b[0m in \u001b[0;36mplot_results\u001b[0;34m(query_res)\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;31m# Storing results:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0mresults_dict\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---> 23\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mpseudo_family\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mformula\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msmearing\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msmearing_units\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmag\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmag_units\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mquery_res\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 24\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mformula\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mresults_dict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0mresults_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mformula\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;31mValueError\u001b[0m: not enough values to unpack (expected 6, got 1)" ] } ], "source": [ "plot_results(results)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.9.5" } }, "nbformat": 4, "nbformat_minor": 5 }