diff --git a/.ci/opensearch/Dockerfile b/.ci/opensearch/Dockerfile new file mode 100644 index 00000000..235c3b02 --- /dev/null +++ b/.ci/opensearch/Dockerfile @@ -0,0 +1,7 @@ +FROM docker:stable + +RUN apk add --update bash + +COPY run-opensearch.sh /run-opensearch.sh + +ENTRYPOINT ["/run-opensearch.sh"] diff --git a/.ci/opensearch/action.yml b/.ci/opensearch/action.yml new file mode 100644 index 00000000..f917a612 --- /dev/null +++ b/.ci/opensearch/action.yml @@ -0,0 +1,33 @@ +name: 'Run OpenSearch' +description: 'This action spins up an Opensearch instance that can be accessed and used in your subsequent steps.' + +inputs: + opensearch-version: + description: 'The version of the OpenSearch you want to run' + required: true + security-enabled: + description: 'Enable or disable HTTPS, enabled by default' + default: 'false' + required: false + nodes: + description: 'Number of nodes in the cluster' + required: false + default: 1 + port: + description: 'Port where you want to run OpenSearch' + required: false + default: 9200 + opensearch-initial-admin-password: + description: 'The password for the user admin in your cluster' + required: false + default: 'myStrongPassword123!' + +runs: + using: 'docker' + image: 'Dockerfile' + env: + OPENSEARCH_VERSION: ${{ inputs.opensearch-version }} + NODES: ${{ inputs.nodes }} + PORT: ${{ inputs.port }} + SECURITY_ENABLED: ${{ inputs.security-enabled }} + OPENSEARCH_INITIAL_ADMIN_PASSWORD: ${{ inputs.opensearch-initial-admin-password }} diff --git a/.ci/opensearch/functions/imports.sh b/.ci/opensearch/functions/imports.sh new file mode 100755 index 00000000..a3ece964 --- /dev/null +++ b/.ci/opensearch/functions/imports.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# +# Sets up all the common variables and imports relevant functions +# +# Version 1.0.1 +# - Initial version after refactor +# From https://github.com/opensearch-project/opensearch-py/blob/main/.ci/functions/imports.sh + +source ./.ci/opensearch/functions/wait-for-container.sh diff --git a/.ci/opensearch/functions/wait-for-container.sh b/.ci/opensearch/functions/wait-for-container.sh new file mode 100755 index 00000000..c1b6d5e1 --- /dev/null +++ b/.ci/opensearch/functions/wait-for-container.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# +# Exposes a routine scripts can call to wait for a container if that container set up a health command +# +# Please source .ci/functions/imports.sh as a whole not just this file +# +# Version 1.0.1 +# - Initial version after refactor +# - Make sure wait_for_contiainer is silent +# From https://github.com/opensearch-project/opensearch-py/blob/main/.ci/functions/wait-for-container.sh + +function container_running { + if [[ "$(docker ps -q -f name=$1)" ]]; then + return 0; + else return 1; + fi +} + +function wait_for_container { + set +x + until ! container_running "$1" || (container_running "$1" && [[ "$(docker inspect -f "{{.State.Health.Status}}" ${1})" != "starting" ]]); do + echo "" + docker inspect -f "{{range .State.Health.Log}}{{.Output}}{{end}}" ${1} + echo -e "\033[34;1mINFO:\033[0m waiting for node $1 to be up\033[0m" + sleep 4; + done; + + # Always show logs if the container is running, this is very useful both on CI as well as while developing + if container_running $1; then + docker logs $1 + fi + + if ! container_running $1 || [[ "$(docker inspect -f "{{.State.Health.Status}}" ${1})" != "healthy" ]]; then + echo -e "\033[31;1mERROR:\033[0m Failed to start $1 in detached mode beyond health checks\033[0m" + echo -e "\033[31;1mERROR:\033[0m dumped the docker log before shutting the node down\033[0m" + return 1 + else + echo + echo -e "\033[32;1mSUCCESS:\033[0m Detached and healthy: ${1}\033[0m" + return 0 + fi +} diff --git a/.ci/opensearch/run-opensearch.sh b/.ci/opensearch/run-opensearch.sh new file mode 100755 index 00000000..4f3678b4 --- /dev/null +++ b/.ci/opensearch/run-opensearch.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +source ./.ci/opensearch/functions/imports.sh +set -euxo pipefail + +if [[ -z $OPENSEARCH_VERSION ]]; then + echo -e "\033[31;1mERROR:\033[0m Required environment variable [OPENSEARCH_VERSION] not set\033[0m" + exit 1 +fi + +OPENSEARCH_REQUIRED_VERSION="latest" +# Starting in 2.12.0, security demo configuration script requires an initial admin password +if [ "$OPENSEARCH_VERSION" != "$OPENSEARCH_REQUIRED_VERSION" ]; then + OPENSEARCH_INITIAL_ADMIN_PASSWORD="admin" +fi + +for (( node=1; node<=${NODES-1}; node++ )) +do + port=$((PORT + $node - 1)) + + if [[ "$SECURITY_ENABLED" == "true" ]]; then + healthcmd="curl -vvv -s --insecure -u admin:$OPENSEARCH_INITIAL_ADMIN_PASSWORD --fail https://localhost:$port/_cluster/health || exit 1" + security=($(cat <<-END + +END + )) + elif [[ "$SECURITY_ENABLED" == "false" ]]; then + healthcmd="curl -vvv -s --fail http://localhost:$port/_cluster/health || exit 1" + security=($(cat <<-END + --env plugins.security.disabled=true +END + )) + fi + + docker run \ + --rm \ + --detach \ + --name="os${node}" \ + --env "cluster.name=docker-opensearch" \ + --env "http.port=${port}" \ + --env discovery.type=single-node \ + --env bootstrap.memory_lock=true \ + --env "OPENSEARCH_JAVA_OPTS=-Xms4g -Xmx4g" \ + --env OPENSEARCH_INITIAL_ADMIN_PASSWORD=$OPENSEARCH_INITIAL_ADMIN_PASSWORD \ + "${security[@]}" \ + --publish "${port}:${port}" \ + --ulimit nofile=65536:65536 \ + --ulimit memlock=-1:-1 \ + --health-cmd="$(echo $healthcmd)" \ + --health-interval=2s \ + --health-retries=20 \ + --health-timeout=2s \ + opensearchproject/opensearch:${OPENSEARCH_VERSION} + + if wait_for_container "os$node"; then + echo -e "\033[32;1mSUCCESS:\033[0m OpenSearch up and running\033[0m" + fi +done diff --git a/.ci/opensearch/test.sh b/.ci/opensearch/test.sh new file mode 100755 index 00000000..c4eb9fd4 --- /dev/null +++ b/.ci/opensearch/test.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +script_path=$(dirname $(realpath -s $0)) +source $script_path/functions/imports.sh +set -euxo pipefail + +echo $script_path/functions/imports.sh diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..94f58d25 --- /dev/null +++ b/.flake8 @@ -0,0 +1,16 @@ +[flake8] +select = B,BLK,C,E,F,I,S,W +max-complexity = 30 +max-line-length = 88 +ignore = E203,W503 # ignore conflicts with black +application-import-names = abcd,tests +import-order-style = google +exclude = + abcd/backends/atoms_pymongo.py, + abcd/frontends, + abcd/model.py, + abcd/parsers/queries_new.py, + abcd/parsers/queries.py, + abcd/parsers/extras.py, + abcd/server, + tests/__init__.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc1cdc84..2a673481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,29 @@ jobs: build: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: python-version: [ "3.9", "3.10", "3.11", "3.12" ] + opensearch: ['1.0.1', '2.0.1', 'latest'] + security-enabled: ["true", "false"] steps: - uses: actions/checkout@v4 + - name: Configure sysctl limits + run: | + sudo swapoff -a + sudo sysctl -w vm.swappiness=1 + sudo sysctl -w fs.file-max=262144 + sudo sysctl -w vm.max_map_count=262144 + + - name: Start OpenSearch + uses: ./.ci/opensearch + with: + port: 9250 + opensearch-version: ${{ matrix.opensearch }} + security-enabled: ${{ matrix.security-enabled }} + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -29,6 +46,10 @@ jobs: - name: Run unit tests run: | poetry run pytest --cov=abcd --cov-report xml --cov-report term:skip-covered + env: + port: 9250 + security_enabled: ${{ matrix.security-enabled }} + opensearch-version: ${{ matrix.opensearch }} - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.4.1 diff --git a/README.md b/README.md index b6ab1c5d..9fe129c4 100644 --- a/README.md +++ b/README.md @@ -3,86 +3,156 @@ [![Doc](https://img.shields.io/badge/docs-master-green.svg)](https://libatoms.github.io/abcd/) [![Build Status](https://travis-ci.org/libAtoms/abcd.svg?branch=master)](https://travis-ci.org/libAtoms/abcd) -Database storage and discovery of atomistic data. +Database storage and discovery of atomistic data. Take a look at the `examples.md` file for.. examples! Main features: -- Configurations that consist of atom positions, elements, forces, and various metadata are stored as a dictionary by a MongoDB backend. -- There is no predefined schema, any combination of keys are allowed for all configurations. -- Two modes: "discovery" and "download". Both use filter-type queries, but in "discovery" mode, summary statistics of the configurations that pass the filter are reported. In "download" mode, the matching configurations are downloaded and exported to a file. -- The "discovery" mode can be used to learn what keys exist in the set of configurations that have passed the current quiery filter. The user can use this to refine the query. -- Complex queries on dictionary key-value pairs are allowed, and their logical combinations. +- Configurations that consist of atom positions, elements, forces, and various metadata are stored as a dictionary by a MongoDB backend. +- There is no predefined schema, any combination of keys are allowed for all configurations. +- Two modes: "discovery" and "download". Both use filter-type queries, but in "discovery" mode, summary statistics of the configurations that pass the filter are reported. In "download" mode, the matching configurations are downloaded and exported to a file. +- The "discovery" mode can be used to learn what keys exist in the set of configurations that have passed the current query filter. The user can use this to refine the query. +- Complex queries on dictionary key-value pairs are allowed, and their logical combinations. ## Installation +### General Setup + creating tables and views -``` + +```sh $ pip install git+https://github.com/libAtoms/abcd.git ``` -## Setup +Example Docker installation on Ubuntu: + +```sh +sudo apt-get update +sudo apt upgrade +sudo apt install docker.io +sudo groupadd docker +sudo usermod -aG docker $USER +newgrp docker # or exit and log in +``` + +Docker can be tested by running: + +```sh +docker run hello-world +``` + +Example Python setup on Ubuntu (pip must be updated for poetry to be used successfully): + +```sh +sudo apt install software-properties-common +sudo add-apt-repository ppa:deadsnakes/ppa +sudo apt install python3.10 +sudo apt-get install python3.10-distutils +sudo apt install python3-virtualenv +virtualenv -p /usr/bin/python3.10 venv_10 +source venv_10/bin/activate +curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10 +``` -If you have an already running mongo server, or install your own, they you are ready to go. Alternatively, +Building and installing ABCD dependencies via poetry: +```sh +git clone https://github.com/libAtoms/abcd.git +curl -sSL https://install.python-poetry.org | python3 - +export PATH="/home/ubuntu/.local/bin:$PATH" +cd abcd +poetry install +poetry build ``` + +### MongoDB + +If you have an already running MongoDB server, or install your own, then you are ready to go. Alternatively, + +```sh docker run -d --rm --name abcd-mongodb -v :/data/db -p 27017:27017 mongo ``` -will download and install a docker and run a database in it. +will download and install a docker and run a database in it. To connect to a mongodb that is already running, use -``` + +```sh abcd login mongodb://localhost ``` If you are running `abcd` inside a docker, and want to connect to a mongodb outside that docker use something like this (example is for Mac OS): -``` +```sh abcd login mongodb://docker.for.mac.localhost ``` The above login command will place create an `~/.abcd` file with the following contents: -``` +```sh {"url": "mongodb://localhost"} ``` -# Remote access +### OpenSearch +If you have an already running OpenSearch server, or install your own, then you are ready to go. Alternatively, -You can set up an `abcd` user on your machine where the database is running, and then access it remotely for discovering data. Make sure you have the `~/.abcd` file created for this user, then put this in the `.ssh/authorized_keys` file (substituting your public key for the last part): +```sh +sudo swapoff -a # optional +sudo sysctl -w vm.swappiness=1 # optional +sudo sysctl -w fs.file-max=262144 # optional +sudo sysctl -w vm.max_map_count=262144 +docker run -d --rm --name abcd-opensearch -v :/data/db -p 9200:9200 --env discovery.type=single-node -it opensearchproject/opensearch:latest ``` -command="/path/to/abcd --remote ${SSH_ORIGINAL_COMMAND}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa your@email + +will download and install an OpenSearch image and run it. The connection can be tested with: + +```sh +curl -vvv -s --insecure -u admin:admin --fail https://localhost:9200 ``` -Then you'll be able to access the database remotely using, e.g. +To connect to an OpenSearch database that is already running, use + +```sh +abcd login opensearch://username:password@localhost +``` + +## Remote access + +You can set up an `abcd` user on your machine where the database is running, and then access it remotely for discovering data. Make sure you have the `~/.abcd` file created for this user, then put this in the `.ssh/authorized_keys` file (substituting your public key for the last part): + +```sh +command="/path/to/abcd --remote ${SSH_ORIGINAL_COMMAND}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa your@email ``` + +Then you'll be able to access the database remotely using, e.g. + +```sh ssh abcd@your.machine summary ``` -# GUI through a browser + visualisation +## GUI through a browser + visualisation The database has a simple GUI, coupled with a visualiser. Data for now needs to be uploaded on the command line, but query can be done through the browsers. Instructions below (they include running `abcd` from a docker too, but of course you can run it outside the docker as well. ) -#### Usage in docker +## Usage in docker Currently a manual uploaded image is available, that was built on 7/2/2020 by Tamas K. Stenczel. To access it: 1. pull the image - ``` + ```sh docker pull stenczelt/projection-abcd:latest ``` 2. create a docker network, which enables the containers to communicate with each other and the outside world as well - ``` + ```sh docker network create --driver bridge abcd-network ``` 3. run the mongo (ABCD) and the visualiser as well - ``` + ```sh docker run -d --rm --name abcd-mongodb-net -v :/data/db -p 27017:27017 --network abcd-network mongo - + docker run -it --rm --name visualiser-dev -p 9999:9999 --network abcd-network stenczelt/projection-abcd ``` NB: You need a a directory where the database files are kept locally and you need to connect this to the mongo @@ -91,8 +161,17 @@ To access it: This will start the visualiser with ABCD integration! Have fun! After usage, for cleanup: -``` + +```sh docker stop visualiser-dev abcd-mongodb-net # stop the containers docker rm visualiser-dev abcd-mongodb-net # remove them if --rm did not docker network rm abcd-network # remove the docker network ``` + +## Testing + +Unit tests are automatically run on push and creation of pull requests. Unit testing using mock databases can also be run in the command line using: + +```sh +python -m unittest tests +``` diff --git a/abcd/__init__.py b/abcd/__init__.py index b8008379..70f8cdc9 100644 --- a/abcd/__init__.py +++ b/abcd/__init__.py @@ -8,6 +8,7 @@ class ConnectionType(Enum): mongodb = 1 http = 2 + opensearch = 3 class ABCD(object): @@ -23,8 +24,10 @@ def from_url(cls, url, **kwargs): r = parse.urlparse(url) logger.info(r) - if r.scheme == "mongodb": + db = r.path.split("/")[1] if r.path else None + db = db if db else "abcd" + if ConnectionType[r.scheme] is ConnectionType.mongodb: conn_settings = { "host": r.hostname, "port": r.port, @@ -33,13 +36,22 @@ def from_url(cls, url, **kwargs): "authSource": "admin", } - db = r.path.split("/")[1] if r.path else None - db = db if db else "abcd" - from abcd.backends.atoms_pymongo import MongoDatabase return MongoDatabase(db_name=db, **conn_settings, **kwargs) + if ConnectionType[r.scheme] is ConnectionType.opensearch: + conn_settings = { + "host": r.hostname, + "port": r.port, + "username": r.username, + "password": r.password, + } + + from abcd.backends.atoms_opensearch import OpenSearchDatabase + + return OpenSearchDatabase(db=db, **conn_settings, **kwargs) + elif r.scheme == "http" or r.scheme == "https": raise NotImplementedError("http not yet supported! soon...") elif r.scheme == "ssh": @@ -57,11 +69,3 @@ def from_url(cls, url, **kwargs): url = "mongodb://mongoadmin:secret@localhost:27017/abcd_new" abcd = ABCD.from_url(url) abcd.print_info() - - # from ase.io import iread - # for atoms in iread('../tutorials/data/bcc_bulk_54_expanded_2_high.xyz', index=slice(1)): - # # Hack to fix the representation of forces - # atoms.calc.results['forces'] = atoms.arrays['force'] - # - # abcd.push(atoms) - # print(atoms) diff --git a/abcd/backends/atoms_opensearch.py b/abcd/backends/atoms_opensearch.py new file mode 100644 index 00000000..9a795cee --- /dev/null +++ b/abcd/backends/atoms_opensearch.py @@ -0,0 +1,1026 @@ +from __future__ import annotations + +from collections.abc import Iterator +from datetime import datetime +from typing import Iterable, Optional, Union +import logging +from os import linesep +from pathlib import Path + +from ase import Atoms +from ase.io import iread +from opensearchpy import ( + OpenSearch, + helpers, + AuthenticationException, + ConnectionTimeout, + RequestError, +) + +from abcd.backends import utils +from abcd.database import AbstractABCD +import abcd.errors +from abcd.model import AbstractModel +from abcd.parsers import extras +from abcd.queryset import AbstractQuerySet + + +logger = logging.getLogger(__name__) + +map_types = { + bool: "bool", + float: "float", + int: "int", + str: "str", + datetime: "date", + dict: "dict", +} + + +class OpenSearchQuery(AbstractQuerySet): + """Class to parse and build queries for OpenSearch.""" + + def __call__(self, query: Optional[Union[dict, str, list]]) -> Optional[dict]: + """ + Parses and builds queries for OpenSearch. + + Parameters + ---------- + query: Optional[Union[dict, str, list]] + Query to be parsed for OpenSearch. If passed as a dictionary, the query is + left unchanged. If passed a string or list, the query is treated as a query + string, based on Lucene query syntax. + + Returns + ------- + Optional[dict] + The parsed query for OpenSearch. + """ + if not query: + query = self.get_default_query() + + if isinstance(query, str): + return self.build_query_string(query) + if isinstance(query, list): + if len(query) == 0: + return None + if query[0] is None: + return None + separator = " AND " + joined_query = separator.join(query) + return self.build_query_string(joined_query) + + logger.info("parsed query: %s", query) + return query if query else None + + @staticmethod + def build_query_string(query: str) -> dict: + """ + Build query_string (Lucene syntax) query. + + Parameters + ---------- + query : str + Query with Lucene syntax. + + Returns + ------- + dict + Parsed query for query_string query. + """ + return {"query_string": {"query": query}} + + @staticmethod + def get_default_query() -> dict: + """ + Defines a default OpenSearch query. Currently, matches all documents. + + Returns + ------- + The default query for OpenSearch. + """ + return {"match_all": {}} + + +class AtomsModel(AbstractModel): + """ + Class to interface between Atoms data and OpenSearch. + + Attributes + ---------- + _client: Optional[OpenSearch] + OpenSearch client. + _index_name: Optional[str] + OpenSearch index name. + """ + + def __init__( + self, + client: Optional[OpenSearch] = None, + index_name: Optional[str] = None, + dict: Optional[dict] = None, + ): + """ + Initialises class. + + Parameters + ---------- + client: Optional[OpenSearch] + OpenSearch client. Default is `None`. + index_name: Optional[str] + OpenSearch index name. Default is `None`. + dict: Optional[dict] + Dictionary of atoms data. Default is `None`. + """ + super().__init__(dict) + + self._client = client + self._index_name = index_name + + @classmethod + def from_atoms( + cls, + client: OpenSearch, + index_name: str, + atoms: Atoms, + extra_info: Optional[dict] = None, + store_calc: bool = True, + ) -> AtomsModel: + """ + Reads and prepares atoms data and extra information for OpenSearch. + + Parameters + ---------- + client: OpenSearch + OpenSearch client. + index_name: str + OpenSearch index name. + atoms: Atoms + Atoms data to be stored. + extra_info: Optional[dict] + Extra information to store in the document with the atoms data. + Default is `None`. + store_calc: bool, optional + Whether to store data from the calculator attached to atoms. + Default is `True`. + + Returns + ------- + Data from atoms and extra information to be saved in OpenSearch. + """ + obj = super().from_atoms(atoms, extra_info, store_calc) + obj._client = client + obj._index_name = index_name + return obj + + @property + def _id(self) -> Optional[str]: + """ + Get the OpenSearch document ID stored in data. + + Returns + ------- + Optional[str] + Current document ID. + """ + return self.get("_id", None) + + def save(self): + """ + Saves data in OpenSearch. If the data being saved includes a document + ID, updates the matching document in OpenSearch with the current data. + """ + body = {} + body.update(self.data) + body["derived"] = self.derived + if self._client is not None: + if not self._id: + self._client.index(index=self._index_name, body=body) + else: + del body["_id"] + body = {"doc": body} + self._client.update(index=self._index_name, id=self._id, body=body) + + def remove(self): + """ + If current data includes a document ID, deletes the matching document + OpenSearch. + """ + if self._client is not None and self._id: + self._client.delete(index=self._index_name, id=self._id) + self.clear() + + +class OpenSearchDatabase(AbstractABCD): + """ + Wrapper to make OpenSearch operations easy. + + Attributes + ---------- + client: OpenSearch + OpenSearch client. + db_name: str + Database name. + index_name: str + OpenSearch index name. + parser: OpenSearchQuery + Query parser and builder for OpenSearch queries. + """ + + def __init__( + self, + host: str = "localhost", + port: int = 9200, + db_name: str = "abcd", + index_name: str = "atoms", + username: str = "admin", + password: str = "admin", + **kwargs, + ): + """ + Initialises class. + + Parameters + ---------- + host: str, optional + Name of OpenSearch host. Default is `localhost`. + port: int, optional + OpenSearch port. Default is `9200`. + db_name: str, optional + Label for OpenSearch database. Used only when printing information. + Default is `abcd`. + index_name: str, optional + Name of OpenSearch index. Default is `atoms`. + username: str, optional + OpenSearch username. Default is `admin`. + password: str, optional + OpenSearch password. Default is `admin`. + """ + super().__init__() + + logger.info((host, port, index_name, username, password, kwargs)) + + client_settings = { + "verify_certs": False, + "ca_certs": None, + "use_ssl": True, + "ssl_assert_hostname": False, + "ssl_show_warn": False, + } + + for key in client_settings: + if key in kwargs: + client_settings[key] = kwargs[key] + + self.client = OpenSearch( + hosts=[{"host": host, "port": port}], + http_auth=(username, password), + **client_settings, + ) + + try: + info = self.client.info() + logger.info("DB info: %s", info) + + except AuthenticationException as err: + raise abcd.errors.AuthenticationError() from err + + except ConnectionTimeout as err: + raise abcd.errors.TimeoutError() from err + + self.db = db_name + self.index_name = index_name + self.create() + self.parser = OpenSearchQuery() + + def info(self): + """ + Gets information from OpenSearch client about the database. + + Returns + ------- + Dictionary of database information. + """ + if self.client.transport.hosts is not None: + host = self.client.transport.hosts[0]["host"] + port = self.client.transport.hosts[0]["port"] + else: + host, port = None, None + + self.refresh() + return { + "host": host, + "port": port, + "db": self.db, + "index": self.index_name, + "number of confs": self.client.count(index=self.index_name)["count"], + "type": "opensearch", + } + + def delete(self, query: Optional[Union[dict, str]] = None): + """ + Deletes documents from the database. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to be deleted. Default is `None`. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + body = {"query": query} + + self.client.delete_by_query( + index=self.index_name, + body=body, + ) + + def destroy(self): + """ + Deletes the current index in OpenSearch. + Ignores errors if the index does not exist. + """ + self.client.indices.delete(index=self.index_name, ignore=404) + + def create(self): + """ + Creates a new index in OpenSearch. + Ignores errors if the index already exists. + """ + self.client.indices.create(index=self.index_name, ignore=400) + + def refresh(self): + """ + Refresh index to ensure recent operations performed are available for search. + """ + self.client.indices.refresh(index=self.index_name) + + def save_bulk(self, actions: Iterable[dict], **kwargs): + """ + Save a collection of documents in bulk. + + Parameters + ---------- + actions: Iterable + Documents to be saved. + """ + request_timeout = kwargs.get("request_timeout", 30) + chunk_size = kwargs.get("chunk_size", 500) + helpers.bulk( + client=self.client, + actions=actions, + index=self.index_name, + chunk_size=chunk_size, + request_timeout=request_timeout, + ) + + def push( + self, + atoms: Union[Atoms, Iterable], + extra_info: Optional[Union[dict, str, list]] = None, + store_calc: bool = True, + **kwargs, + ): + """ + Save data from atoms object(s) to database. + + Parameters + ---------- + atoms: Union[Atoms, Iterable] + extra_info: Optional[Union[dict, str, list]] + Extra information to store in the document with the atoms data. + Default is `None`. + store_calc: bool, optional + Whether to store data from the calculator attached to atoms. + Default is `True`. + """ + if extra_info and isinstance(extra_info, str): + extra_info = extras.parser.parse(extra_info) # type: ignore + if extra_info and isinstance(extra_info, list): + for i, info in enumerate(extra_info): + if isinstance(info, str): + extra_info[i] = extras.parser.parse(info) + + if isinstance(atoms, Atoms): + data = AtomsModel.from_atoms( + self.client, + self.index_name, + atoms, + extra_info=extra_info, # type: ignore + store_calc=store_calc, + ) + data.save() + + elif isinstance(atoms, Iterator) or isinstance(atoms, list): + actions = [] + for i, item in enumerate(atoms): + if isinstance(extra_info, list): + info = extra_info[i] + else: + info = extra_info + data = AtomsModel.from_atoms( + self.client, + self.index_name, + item, + extra_info=info, # type: ignore + store_calc=store_calc, + ) + actions.append(data.data) + actions[-1]["derived"] = data.derived + self.save_bulk(actions, **kwargs) + + def upload( + self, + file: Path, + extra_infos: Union[Iterable, dict] = (), + store_calc: bool = True, + ): + """ + Upload data from a file to the database. + + Parameters + ---------- + file: Path + Path to file to be uploaded + extra_infos: Union[Iterable, dict] + Extra information to store in the document with the atoms data. + Default is `()`. + store_calc: bool, optional + Whether to store data from the calculator attached to atoms. + Default is `True`. + """ + + if isinstance(file, str): + file = Path(file) + + extra_info = {} + for info in extra_infos: + extra_info.update(extras.parser.parse(info)) + + extra_info["filename"] = str(file) + + data = iread(str(file)) + self.push(data, extra_info, store_calc=store_calc) + + def get_items(self, query: Optional[Union[dict, str]] = None) -> Iterator[dict]: + """ + Get data as a dictionary from documents in the database. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to get data from. Default is `None`. + + Returns + ------- + Iterator[dict] + Iterator for dictionary of data. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + query = { + "query": query, + } + + for hit in helpers.scan( + self.client, + index=self.index_name, + query=query, + ): + yield {"_id": hit["_id"], **hit["_source"]} + + def get_atoms(self, query: Optional[Union[dict, str]] = None) -> Iterator[Atoms]: + """ + Get data as Atoms object from documents in the database. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to get data from. Default is `None`. + + Returns + ------- + Iterator[Atoms] + Generator for AtomsModel object of data. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + query = { + "query": query, + } + + for hit in helpers.scan( + self.client, + index=self.index_name, + query=query, + ): + yield AtomsModel(dict=hit["_source"]).to_ase() + + def count(self, query: Optional[Union[dict, str]] = None, timeout=30.0) -> int: + """ + Counts number of documents in the database. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to be counted. Default is `None`. + timeout: float + Timeout for request in seconds. + + Returns + ------- + Count of number of documents. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + body = { + "query": query, + } + + return self.client.count(index=self.index_name, body=body, timeout=timeout)[ + "count" + ] + + def _get_props_from_source( + self, + names: Union[str, list[str]], + query: Optional[Union[dict, str]] = None, + ) -> dict: + """ + Gets all values of specified properties using the original data from _source. + + Parameters + ---------- + names: Union[str, list[str]] + Name or list of names of properties to return. + query: Optional[Union[dict, str]] + Query to filter documents to get properties from. Default is `None`. + + Returns + ------- + dict + Dictionary of lists of values for the specified properties. + """ + props = {} + hits = [ + dict(hit["_source"].items()) + for hit in helpers.scan( + self.client, + index=self.index_name, + query=query, + stored_fields=names, + _source=names, + ) + if "_source" in hit and all(name in hit["_source"] for name in names) + ] + for name in names: + props[name] = [hit[name] for hit in hits] + return props + + def property( + self, + names: Union[str, list[str]], + allow_flatten: bool = True, + query: Optional[Union[dict, str]] = None, + ) -> Union[dict, list]: + """ + Gets all values of specified properties for matching documents in the database. + + Parameters + ---------- + names: Union[str, list[str]] + Name or list of names of properties to return. + allow_flatten: bool = True + Whether to allow arrays to be returned flattened. There is no guarantee + for the order of returned values. Default is `True`. + query: Optional[Union[dict, str]] + Query to filter documents to get properties from. Default is `None`. + + Returns + ------- + Union[dict, list] + Dictionary of lists of values for the specified properties, or list + if only one property is given. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + query = { + "query": query, + } + + if isinstance(names, str): + names = [names] + names = [format(name) for name in names] + + # Try to use docvalue_fields to avoid loading entire document + # But not all datatypes supported by default + if allow_flatten: + props = {} + try: + hits = [ + dict(hit["fields"].items()) + for hit in helpers.scan( + self.client, + index=self.index_name, + query=query, + _source=False, + stored_fields="_none_", + docvalue_fields=names, + ) + if "fields" in hit and all(name in hit["fields"] for name in names) + ] + for name in names: + props[name] = [ + hit[name][0] if len(hit[name]) == 1 else hit[name] + for hit in hits + ] + + except RequestError: + props = self._get_props_from_source(names, query) + + # Use _source to ensure arrays are not flattened + else: + props = self._get_props_from_source(names, query) + + if len(names) == 1: + return props[names[0]] + return props + + def count_property(self, name, query: Optional[Union[dict, str]] = None) -> dict: + """ + Counts values of a specified property for matching documents in the + database. This method much faster than performing a Count on the list + returned by self.property, so this method should be used preferentially. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to count properties from. Default is `None`. + + Returns + ------- + Dictionary of values and counts for the specified property for all + matching documents. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + + body = { + "size": 0, + "query": query, + "aggs": { + format(name): { + "terms": { + "field": format(name), + "size": 10000, # Use composite for all results? + }, + }, + }, + } + + prop = {} + + for val in self.client.search(index=self.index_name, body=body)["aggregations"][ + format(name) + ]["buckets"]: + prop[val["key"]] = val["doc_count"] + + return prop + + def properties(self, query: Optional[Union[dict, str]] = None) -> dict: + """ + Gets lists of all properties from matching documents, separated into + info, derived, and array properties. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to get properties from. Default is `None`. + + Returns + ------- + Dictionary of properties, with keys corresponding to info, derived, + and arrays of properties, and values corresponding to a list of + the properties of that type. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + + properties = {} + + for prop in self.client.indices.get_mapping(index=self.index_name)[ + self.index_name + ]["mappings"]["properties"].keys(): + body = { + "size": 0, + "query": query, + "aggs": { + "info_keys": { + "filter": {"term": {"derived.info_keys.keyword": prop}}, + }, + "derived_keys": { + "filter": {"term": {"derived.derived_keys.keyword": prop}}, + }, + "arrays_keys": { + "filter": {"term": {"derived.arrays_keys.keyword": prop}}, + }, + }, + } + + res = self.client.search( + index=self.index_name, + body=body, + ) + + for label in ("info_keys", "derived_keys", "arrays_keys"): + count = res["aggregations"][label]["doc_count"] + if count > 0: + key = label.split("_", maxsplit=1)[0] + if key in properties: + properties[key].append(prop) + else: + properties[key] = [prop] + + return properties + + def get_type_of_property(self, prop: str, category: str) -> str: + """ + Gets type of a property, given its category. + + Parameters + ---------- + prop: str + Name of the property. + catagory: str + Name of property's category. Current options are `info`, `derived`, + and `arrays`. + + Returns + ------- + Type of the property. + """ + atoms = self.client.search( + index=self.index_name, + body={"size": 1, "query": {"exists": {"field": prop}}}, + ) + + data = atoms["hits"]["hits"][0]["_source"][prop] + + if category == "arrays": + if isinstance(data[0], list): + return "array({}, N x {})".format( + map_types[type(data[0][0])], len(data[0]) + ) + return "vector({}, N)".format(map_types[type(data[0])]) + + if isinstance(data, list): + if isinstance(data[0], list): + if isinstance(data[0][0], list): + return "list(list(...)" + return "array({})".format(map_types[type(data[0][0])]) + return "vector({})".format(map_types[type(data[0])]) + return "scalar({})".format(map_types[type(data)]) + + def count_properties(self, query: Optional[Union[dict, str]] = None) -> dict: + """ + Counts all properties from matching documents. + + Parameters + ---------- + query: Optional[Union[dict, str]] + Query to filter documents to count properties from. Default is `None`. + + Returns + ------- + Dictionary of properties, with keys property names, and values + corresponding to their counts, categories and data types. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + properties = {} + + try: + keys = self.client.indices.get_mapping(index=self.index_name)[ + self.index_name + ]["mappings"]["properties"].keys() + except KeyError: + return properties + + for key in keys: + body = { + "size": 0, + "query": query, + "aggs": { + "info_keys": { + "filter": {"term": {"derived.info_keys.keyword": key}}, + }, + "derived_keys": { + "filter": {"term": {"derived.derived_keys.keyword": key}}, + }, + "arrays_keys": { + "filter": {"term": {"derived.arrays_keys.keyword": key}}, + }, + }, + } + + res = self.client.search( + index=self.index_name, + body=body, + ) + + for label in ("info_keys", "derived_keys", "arrays_keys"): + count = res["aggregations"][label]["doc_count"] + if count > 0: + properties[key] = { + "count": count, + "category": label.split("_", maxsplit=1)[0], + "dtype": self.get_type_of_property( + key, label.split("_", maxsplit=1)[0] + ), + } + + return properties + + def add_property(self, data: dict, query: Optional[Union[dict, str]] = None): + """ + Adds properties to matching documents. + + Parameters + ---------- + data: dict + Property key-value pairs to be added to matching documents. + query: Optional[Union[dict, str]] + Query to filter documents to add properties to. Default is `None`. + """ + query = self.parser(query) + logger.info("add: data=%s, query=%s", data, query) + + script_txt = "ctx._source.derived.info_keys.addAll(params.keys);" + for key, val in data.items(): + script_txt += f"ctx._source.{key} = '{val}';" + + body = { + "script": { + "source": script_txt, + "lang": "painless", + "params": {"keys": list(data.keys())}, + }, + "query": query, + } + + self.client.update_by_query( + index=self.index_name, + body=body, + ) + + def rename_property( + self, name: str, new_name: str, query: Optional[Union[dict, str]] = None + ): + """ + Renames property for all matching documents. + + Parameters + ---------- + name: str + Current name of property to be renamed. + new_name: str + New name of property to be renamed. + query: Optional[Union[dict, str]] + Query to filter documents to rename property. Default is `None`. + """ + query = self.parser(query) + logger.info("rename: query=%s, old=%s, new=%s", query, name, new_name) + + script_txt = "if (!ctx._source.containsKey(params.new_name)) { " + script_txt += ( + f"ctx._source.{new_name} = ctx._source.{name};" + " ctx._source.remove(params.name);" + " for (int i=0; i Optional[dict]: + """ + Calculate histogram statistics for a property from all matching documents. + + Parameters + ---------- + name: str + Name of property. + query: Optional[Union[dict, str]] + Query to filter documents. Default is `None`. + + Returns + ------- + Optional[dict] + Dictionary containing histogram statistics, including the number of + bins, edges, counts, min, max, and standard deviation. + """ + query = self.parser(query) + logger.info("parsed query: %s", query) + + data = self.property(name, query=query) + return utils.histogram(name, data, **kwargs) + + def __repr__(self): + """ + OpenSearch class representation. + + Returns + ------- + String for OpenSearch class representation, containing the connected + database host, port, and index name. + """ + if self.client.transport.hosts is not None: + host = self.client.transport.hosts[0]["host"] + port = self.client.transport.hosts[0]["port"] + else: + host, port = None, None + + return ( + f"{self.__class__.__name__}(" + f"url={host}:{port}, " + f"index={self.index_name}) " + ) + + def _repr_html_(self): + """ + Jupyter notebook representation of OpenSearch class. + + Returns + ------- + String for HTML representation. + """ + return "ABCD OpenSearch database" + + def print_info(self): + """ + Show basic information about the connected OpenSearch database. + """ + out = linesep.join( + [ + "{:=^50}".format(" ABCD OpenSearch "), + "{:>10}: {}".format("type", "opensearch"), + linesep.join("{:>10}: {}".format(k, v) for k, v in self.info().items()), + ] + ) + + print(out) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +if __name__ == "__main__": + db = OpenSearchDatabase(username="admin", password="admin") + print(db.info()) diff --git a/abcd/backends/atoms_properties.py b/abcd/backends/atoms_properties.py new file mode 100644 index 00000000..7372e797 --- /dev/null +++ b/abcd/backends/atoms_properties.py @@ -0,0 +1,201 @@ +from __future__ import annotations +import pandas as pd +import numpy as np +from typing import Union +from pathlib import Path +import chardet + + +class Properties: + """ + Wrapper to identify and manipulate properties to be passed + as extra_info to the database. + + Attributes + ---------- + data_file: Union[str, Path] + Name or path to data file containing properties. Treated as a csv file + by default, but Excel spreadsheets may also be read. + store_struct_file: bool + Whether to construct a filename for each structure. + struct_file_template: str + Template string for path to files containing structure. + struct_name_label: str + Field name in data file containing values for `struct_name`. + df: pd.Dataframe + Dataframe containing loaded property data from data file. + units: Union[dict, None], optional + Units. + struct_files: list[str] + List containing a filename for each structure in the dataframe. + encoding: str, optional + Encoding of csv file to be read. Default is `utf-8`. + """ + + def __init__( + self, + data_file: Union[str, Path], + store_struct_file: bool = False, + struct_file_template: Union[str, None] = None, + struct_name_label: Union[str, None] = None, + units: Union[dict, None] = None, + infer_units: bool = False, + encoding: str = "utf-8", + ): + """ + Initialises class. + + Parameters + ---------- + data_file: Union[str, Path] + Path or filename of data file containing properties to be loaded. + Assumed to be a csv file by default, but Excel spreadsheets may + also be read. + store_struct_file: bool, optional + If true, use struct_file_template and struct_name_label to + construct filename for each structure. Default is `False`. + struct_file_template: Union[str, None], optiona + Template string for path to files containing structure. + Required only if store_struct_file is True. + Template must contain `{struct_name}`, to ensure a unique file + for each structure. Default is `None`. + struct_name_label: Union[str, None], optional + Field name in data file containing values for `struct_name`. + Required only if store_struct_file is True. Default is `None`. + units: Union[dict, None], optional + Units for fields in data file. If unspecified, _separate_units() + is used to identify units in field names. Default is `None`. + infer_units: bool, optional + Whether to attempt to infer units from field names in the + dataframe. Unused if units is not `None`. Default is `False`. + encoding: str, optional + Encoding of file to be read. Default is `utf-8`. + For pandas==1.2, setting this to `None` means `errors='replace'` + is passed to `open()`, which replaces invalid characters with + the replacement character. Otherwise, `errors='strict'` is passed + to `open()`, which means UnicodeDecodeError are thrown if the + encoding is wrong. + For pandas==1.3, `encoding` no longer defines how errors are + handled. `encoding_errors` instead defaults to `strict`, which has + the same effect as non-None values of `encoding` for pandas==1.2. + """ + self.data_file = data_file + self.encoding = encoding + try: + self.df = pd.read_csv(self.data_file, encoding=self.encoding) + except UnicodeDecodeError: + detected = chardet.detect(Path(self.data_file).read_bytes()) + raise ValueError( + f"File cannot be decoded using encoding: {self.encoding}." + f" Detected encoding: {detected}." + ) + except pd.errors.ParserError: + self.df = pd.read_excel(self.data_file, header=0) + + self.df.replace({np.nan: None}, inplace=True) + + if units is not None: + for key in units: + if key not in self.df.columns.values: + raise ValueError( + f"Invalid field name: {key}. Keys in `units` must " + "correspond to field names in the loaded data." + ) + self.units = units + elif infer_units: + self._separate_units() + else: + self.units = None + + self.store_struct_file = store_struct_file + if self.store_struct_file: + if struct_file_template is None: + raise ValueError( + "`struct_file_template` must be specified if " + "store_struct_file is True." + ) + self.struct_file_template = struct_file_template + + if struct_name_label is None: + raise ValueError( + "`struct_name_label` must be specified if store_struct_file is" + " True." + ) + self.struct_name_label = struct_name_label + self.set_struct_files() + + def _separate_units(self): + """ + Parse field names to determine units. + """ + columns = [] + self.units = {} + for column in list(self.df.columns.values): + if "," in column: + column_name = column.split(",")[0].strip() + self.units[column_name] = column.split(",")[1].strip() + elif "(" in column: + column_name = column.split("(")[0].strip() + self.units[column_name] = column.split("(")[1].strip()[:-1] + else: + column_name = column + + columns.append(column_name) + + self.df.columns = columns + + def set_struct_files(self): + """ + Sets a list containing a filename for each structure in the dataframe. + """ + self.struct_files = [] + + for i in range(len(self.df)): + try: + struct_name = self.df.iloc[i][self.struct_name_label] + except KeyError: + raise ValueError( + f"{self.struct_name_label} is not a valid column in " + "the data loaded." + ) + struct_file = self.get_struct_file(struct_name) + self.struct_files.append(struct_file) + + def get_struct_file(self, struct_name: str) -> str: + """ + Evaluate struct_file_template to determine structure filename + for current structure. + + Parameters + ---------- + struct_name: str + Name of current structure. + + Returns + ------- + Filename for the current structure. + """ + if "{struct_name}" not in self.struct_file_template: + raise ValueError( + "'struct_name' must be a variable in the template file: " + f"{self.struct_file_template}" + ) + return eval(f"f'{self.struct_file_template}'") + + def to_list(self) -> list[dict]: + """ + Convert dataframe into list of properties for each structure. + + Returns + ------- + List of property dictionaries for each structure in the dataframe. + """ + properties_list = [] + for i in range(len(self.df)): + properties = self.df.iloc[i].to_dict() + if self.units is not None: + properties["units"] = self.units + properties_list.append( + {key: value for key, value in properties.items() if value is not None} + ) + return properties_list diff --git a/abcd/backends/atoms_pymongo.py b/abcd/backends/atoms_pymongo.py index 993c6eb0..f51311f6 100644 --- a/abcd/backends/atoms_pymongo.py +++ b/abcd/backends/atoms_pymongo.py @@ -1,27 +1,23 @@ -import types +from datetime import datetime import logging -import numpy as np - -from typing import Union, Iterable from os import linesep -from operator import itemgetter -from collections import Counter -from datetime import datetime +from pathlib import Path +import types +from typing import Union, Iterable from ase import Atoms from ase.io import iread +from bson import ObjectId +from pymongo import MongoClient +import pymongo.errors +from abcd.backends import utils +from abcd.database import AbstractABCD import abcd.errors from abcd.model import AbstractModel -from abcd.database import AbstractABCD -from abcd.queryset import AbstractQuerySet from abcd.parsers import extras +from abcd.queryset import AbstractQuerySet -import pymongo.errors -from pymongo import MongoClient -from bson import ObjectId - -from pathlib import Path logger = logging.getLogger(__name__) @@ -135,6 +131,15 @@ def __call__(self, ast): p = parser(ast) return self.visit(p) + elif isinstance(ast, list): + from abcd.parsers.queries import parser + + if len(ast) == 0: + return {} + else: + ast = ("AND", *[parser(q) for q in ast]) + return self.visit(ast) + return self.visit(ast) if ast else {} @@ -222,7 +227,6 @@ def destroy(self): self.collection.drop() def push(self, atoms: Union[Atoms, Iterable], extra_info=None, store_calc=True): - if extra_info and isinstance(extra_info, str): extra_info = extras.parser.parse(extra_info) @@ -234,15 +238,17 @@ def push(self, atoms: Union[Atoms, Iterable], extra_info=None, store_calc=True): # self.collection.insert_one(data) elif isinstance(atoms, types.GeneratorType) or isinstance(atoms, list): - - for item in atoms: + for i, item in enumerate(atoms): + if isinstance(extra_info, list): + info = extra_info[i] + else: + info = extra_info data = AtomsModel.from_atoms( - self.collection, item, extra_info=extra_info, store_calc=store_calc + self.collection, item, extra_info=info, store_calc=store_calc ) data.save() def upload(self, file: Path, extra_infos=None, store_calc=True): - if isinstance(file, str): file = Path(file) @@ -435,9 +441,8 @@ def delete_property(self, name, query=None): ) def hist(self, name, query=None, **kwargs): - data = self.property(name, query) - return histogram(name, data, **kwargs) + return utils.histogram(name, data, **kwargs) def exec(self, code, query=None): # TODO: Separate python environment with its own packages loaded @@ -480,139 +485,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass -def histogram(name, data, **kwargs): - if not data: - return None - - elif data and isinstance(data, list): - - ptype = type(data[0]) - - if not all(isinstance(x, ptype) for x in data): - print("Mixed type error of the {} property!".format(name)) - return None - - if ptype == float: - bins = kwargs.get("bins", 10) - return _hist_float(name, data, bins) - - elif ptype == int: - bins = kwargs.get("bins", 10) - return _hist_int(name, data, bins) - - elif ptype == str: - return _hist_str(name, data, **kwargs) - - elif ptype == datetime: - bins = kwargs.get("bins", 10) - return _hist_date(name, data, bins) - - else: - print( - "{}: Histogram for list of {} types are not supported!".format( - name, type(data[0]) - ) - ) - logger.info( - "{}: Histogram for list of {} types are not supported!".format( - name, type(data[0]) - ) - ) - - else: - logger.info( - "{}: Histogram for {} types are not supported!".format(name, type(data)) - ) - return None - - -def _hist_float(name, data, bins=10): - data = np.array(data) - hist, bin_edges = np.histogram(data, bins=bins) - - return { - "type": "hist_float", - "name": name, - "bins": bins, - "edges": bin_edges, - "counts": hist, - "min": data.min(), - "max": data.max(), - "median": data.mean(), - "std": data.std(), - "var": data.var(), - } - - -def _hist_date(name, data, bins=10): - hist_data = np.array([t.timestamp() for t in data]) - hist, bin_edges = np.histogram(hist_data, bins=bins) - - fromtimestamp = datetime.fromtimestamp - - return { - "type": "hist_date", - "name": name, - "bins": bins, - "edges": [fromtimestamp(d) for d in bin_edges], - "counts": hist, - "min": fromtimestamp(hist_data.min()), - "max": fromtimestamp(hist_data.max()), - "median": fromtimestamp(hist_data.mean()), - "std": fromtimestamp(hist_data.std()), - "var": fromtimestamp(hist_data.var()), - } - - -def _hist_int(name, data, bins=10): - data = np.array(data) - delta = max(data) - min(data) + 1 - - if bins > delta: - bins = delta - - hist, bin_edges = np.histogram(data, bins=bins) - - return { - "type": "hist_int", - "name": name, - "bins": bins, - "edges": bin_edges, - "counts": hist, - "min": data.min(), - "max": data.max(), - "median": data.mean(), - "std": data.std(), - "var": data.var(), - } - - -def _hist_str(name, data, bins=10, truncate=20): - n_unique = len(set(data)) - - if truncate: - # data = (item[:truncate] for item in data) - data = ( - item[:truncate] + "..." if len(item) > truncate else item for item in data - ) - - data = Counter(data) - - if bins: - labels, counts = zip(*sorted(data.items(), key=itemgetter(1, 0), reverse=True)) - else: - labels, counts = zip(*data.items()) - - return { - "type": "hist_str", - "name": name, - "total": sum(data.values()), - "unique": n_unique, - "labels": labels[:bins], - "counts": counts[:bins], - } - - if __name__ == "__main__": # import json # from ase.io import iread diff --git a/abcd/backends/utils.py b/abcd/backends/utils.py new file mode 100644 index 00000000..e55471eb --- /dev/null +++ b/abcd/backends/utils.py @@ -0,0 +1,131 @@ +from collections import Counter +from datetime import datetime +import logging +from operator import itemgetter + +import numpy as np + +logger = logging.getLogger(__name__) + + +def histogram(name, data, **kwargs): + if not data: + return None + + if isinstance(data, list): + ptype = type(data[0]) + + if not all(isinstance(x, ptype) for x in data): + print("Mixed type error of the %s property!", name) + return None + + if ptype == float: + bins = kwargs.get("bins", 10) + return _hist_float(name, data, bins) + + if ptype == int: + bins = kwargs.get("bins", 10) + return _hist_int(name, data, bins) + + if ptype == str: + return _hist_str(name, data, **kwargs) + + if ptype == datetime: + bins = kwargs.get("bins", 10) + return _hist_date(name, data, bins) + + print( + "%s: Histogram for list of %s types are not supported!", name, type(data[0]) + ) + logger.info( + "%s: Histogram for list of %s types are not supported!", name, type(data[0]) + ) + + logger.info("%s: Histogram for %s types are not supported!", name, type(data)) + return None + + +def _hist_float(name, data, bins=10): + data = np.array(data) + hist, bin_edges = np.histogram(data, bins=bins) + + return { + "type": "hist_float", + "name": name, + "bins": bins, + "edges": bin_edges, + "counts": hist, + "min": data.min(), + "max": data.max(), + "median": data.mean(), + "std": data.std(), + "var": data.var(), + } + + +def _hist_date(name, data, bins=10): + hist_data = np.array([t.timestamp() for t in data]) + hist, bin_edges = np.histogram(hist_data, bins=bins) + + fromtimestamp = datetime.fromtimestamp + + return { + "type": "hist_date", + "name": name, + "bins": bins, + "edges": [fromtimestamp(d) for d in bin_edges], + "counts": hist, + "min": fromtimestamp(hist_data.min()), + "max": fromtimestamp(hist_data.max()), + "median": fromtimestamp(hist_data.mean()), + "std": fromtimestamp(hist_data.std()), + "var": fromtimestamp(hist_data.var()), + } + + +def _hist_int(name, data, bins=10): + data = np.array(data) + delta = max(data) - min(data) + 1 + + bins = min(bins, delta) + + hist, bin_edges = np.histogram(data, bins=bins) + + return { + "type": "hist_int", + "name": name, + "bins": bins, + "edges": bin_edges, + "counts": hist, + "min": data.min(), + "max": data.max(), + "median": data.mean(), + "std": data.std(), + "var": data.var(), + } + + +def _hist_str(name, data, bins=10, truncate=20): + n_unique = len(set(data)) + + if truncate: + # data = (item[:truncate] for item in data) + data = ( + item[:truncate] + "..." if len(item) > truncate else item for item in data + ) + + data = Counter(data) + + if bins: + labels, counts = zip(*sorted(data.items(), key=itemgetter(1, 0), reverse=True)) + else: + labels, counts = zip(*data.items()) + + return { + "type": "hist_str", + "name": name, + "total": sum(data.values()), + "unique": n_unique, + "labels": labels[:bins], + "counts": counts[:bins], + } diff --git a/abcd/frontends/commandline/commands.py b/abcd/frontends/commandline/commands.py index de158a5c..6c3e0edf 100644 --- a/abcd/frontends/commandline/commands.py +++ b/abcd/frontends/commandline/commands.py @@ -4,23 +4,22 @@ import numpy as np from abcd.frontends.commandline.decorators import check_remote, init_config, init_db +from abcd.backends.atoms_opensearch import OpenSearchDatabase logger = logging.getLogger(__name__) @init_config -def login(*, config, name, url, **kwargs): +def login(*, config, name, url, disable_ssl=False, **kwargs): logger.info( - "login args: \nconfig:{}, name:{}, url:{}, kwargs:{}".format( - config, name, url, kwargs - ) + "login args: \nconfig:%s, name:%s, url:%s, kwargs:%s", config, name, url, kwargs ) from abcd import ABCD - db = ABCD.from_url(url=url) + db = ABCD.from_url(url=url, use_ssl=(not disable_ssl)) info = db.info() - config["url"] = url + config.update(url=url, use_ssl=not disable_ssl) config.save() print("Successfully connected to the database!") @@ -36,7 +35,7 @@ def login(*, config, name, url, **kwargs): @init_config @init_db def download(*, db, query, fileformat, filename, **kwargs): - logger.info("download\n kwargs: {}".format(kwargs)) + logger.info("download\n kwargs: %s", kwargs) from ase.io import write @@ -51,7 +50,7 @@ def download(*, db, query, fileformat, filename, **kwargs): @init_db @check_remote def delete(*, db, query, yes, **kwargs): - logger.info("delete\n kwargs: {}".format(kwargs)) + logger.info("delete\n kwargs: %s", kwargs) if not yes: print( @@ -79,10 +78,10 @@ def upload(*, db, path, extra_infos, ignore_calc_results, **kwargs): elif path.is_dir(): for file in path.glob(".xyz"): - logger.info("Uploaded file: {}".format(file)) + logger.info("Uploaded file: %s", file) db.upload(file, extra_infos, store_calc=calculator) else: - logger.info("No file found: {}".format(path)) + logger.info("No file found: %s", path) raise FileNotFoundError() else: @@ -92,8 +91,8 @@ def upload(*, db, path, extra_infos, ignore_calc_results, **kwargs): @init_config @init_db def summary(*, db, query, print_all, bins, truncate, props, **kwargs): - logger.info("summary\n kwargs: {}".format(kwargs)) - logger.info("query: {}".format(query)) + logger.info("summary\n kwargs: %s", kwargs) + logger.info("query: %s", query) if print_all: truncate = None @@ -121,7 +120,6 @@ def summary(*, db, query, print_all, bins, truncate, props, **kwargs): f = Formater() if props_list is None: - props = db.count_properties(query=query) labels, categories, dtypes, counts = [], [], [], [] @@ -158,8 +156,8 @@ def summary(*, db, query, print_all, bins, truncate, props, **kwargs): @init_config @init_db def show(*, db, query, print_all, props, **kwargs): - logger.info("show\n kwargs: {}".format(kwargs)) - logger.info("query: {}".format(query)) + logger.info("show\n kwargs: %s", kwargs) + logger.info("query: %s", query) if not props: print("Please define at least on property by using the -p option!") @@ -184,14 +182,22 @@ def key_add(*, db, query, keys, **kwargs): data = parser.parse(keys) if query: - test = ("AND", query, ("OR", *(("NAME", key) for key in data.keys()))) + if isinstance(db, OpenSearchDatabase): + test = [ + f"{query} AND ({' OR '.join(f'{key}:*' for key in data)})" + for query in query + ] + else: + test = ("AND", query, ("OR", *(("NAME", key) for key in data))) + elif isinstance(db, OpenSearchDatabase): + test = " OR ".join(f"{key}:*" for key in data) else: - test = ("OR", *(("NAME", key) for key in data.keys())) + test = ("OR", *(("NAME", key) for key in data)) if db.count(query=test): print( - "The new key already exist for the given query! " - "Please make sure that the target key name don't exist" + "The new key already exists for the given query! " + "Please make sure that the target key name doesn't exist" ) exit(1) @@ -221,7 +227,13 @@ def key_delete(*, db, query, yes, keys, **kwargs): keys = " ".join(keys) data = parser.parse(keys) - query = ("AND", query, ("OR", *(("NAME", key) for key in data.keys()))) + if isinstance(db, OpenSearchDatabase): + query = [ + f"{query} AND ({' OR '.join(f'{key}:*' for key in data)})" + for query in query + ] + else: + query = ("AND", query, ("OR", *(("NAME", key) for key in data))) if not yes: print( @@ -231,7 +243,7 @@ def key_delete(*, db, query, yes, keys, **kwargs): ) exit(1) - for k in keys: + for k in data: db.delete_property(k, query=query) @@ -255,9 +267,7 @@ def server(*, abcd_url, url, api_only, **kwargs): from urllib.parse import urlparse from abcd.server.app import create_app - logger.info( - "SERVER - abcd: {}, url: {}, api_only:{}".format(abcd_url, url, api_only) - ) + logger.info("SERVER - abcd: %s, url: %s, api_only: %s", abcd_url, url, api_only) if api_only: print("Not implemented yet!") @@ -269,6 +279,12 @@ def server(*, abcd_url, url, api_only, **kwargs): app.run(host=o.hostname, port=o.port) +@init_config +@init_db +def refresh(*, db, **kwargs): + db.refresh() + + class Formater(object): partialBlocks = ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"] # char=pb @@ -278,8 +294,8 @@ def title(self, title): def describe(self, data): if data["type"] == "hist_float": print( - "{} count: {} min: {:11.4e} med: {:11.4e} max: {:11.4e} std: {:11.4e} var:{" - ":11.4e}".format( + "{} count: {} min: {:11.4e} med: {:11.4e} max: {:11.4e} std: {:11.4e}" + " var:{:11.4e}".format( data["name"], sum(data["counts"]), data["min"], @@ -321,7 +337,6 @@ def hist_float(self, bin_edges, counts, width_hist=40): ) def hist_int(self, bin_edges, counts, width_hist=40): - ratio = width_hist / max(counts) width_count = len(str(max(counts))) @@ -373,7 +388,6 @@ def hist_str(self, total, counts, labels, width_hist=40): ) def hist_labels(self, counts, categories, dtypes, labels, width_hist=40): - width_count = len(str(max(counts))) ratio = width_hist / max(counts) for label, count, dtype in zip(labels, counts, dtypes): diff --git a/abcd/frontends/commandline/config.py b/abcd/frontends/commandline/config.py index 3aa21bea..4a2573e5 100644 --- a/abcd/frontends/commandline/config.py +++ b/abcd/frontends/commandline/config.py @@ -20,7 +20,6 @@ def from_json(cls, filename): @classmethod def load(cls): - if ( os.environ.get("ABCD_CONFIG") and Path(os.environ.get("ABCD_CONFIG")).is_file() diff --git a/abcd/frontends/commandline/decorators.py b/abcd/frontends/commandline/decorators.py index c2439be7..ce509004 100644 --- a/abcd/frontends/commandline/decorators.py +++ b/abcd/frontends/commandline/decorators.py @@ -1,9 +1,7 @@ import logging - +import functools from abcd import ABCD - from abcd.frontends.commandline.config import Config -from abcd.parsers.queries import parser logger = logging.getLogger(__name__) @@ -11,6 +9,7 @@ def init_config(func): config = Config.load() + @functools.wraps(func) def wrapper(*args, **kwargs): func(*args, config=config, **kwargs) @@ -18,39 +17,38 @@ def wrapper(*args, **kwargs): def init_db(func): + @functools.wraps(func) def wrapper(*args, config, **kwargs): url = config.get("url", None) + use_ssl = config.get("use_ssl", None) if url is None: print("Please use abcd login first!") exit(1) - db = ABCD.from_url(url=url) + if use_ssl is None: + print("use_ssl has not been saved. Please login again") + exit(1) + + db = ABCD.from_url(url=url, use_ssl=use_ssl) # TODO: AST.from_string() ?! - # TODO: parser should accept list # TODO: better ast optimisation query_list = [] for q in kwargs.pop("default_query", []): - query_list.append(parser(q)) + query_list.append(q) for q in kwargs.pop("query", []): - query_list.append(parser(q)) - - if not query_list: - query = None - elif len(query_list) == 1: - query = query_list[0] - else: - query = ("AND", *query_list) + query_list.append(q) - func(*args, db=db, query=query, **kwargs) + func(*args, db=db, query=query_list, **kwargs) return wrapper def check_remote(func): + @functools.wraps(func) def wrapper(*args, **kwargs): if kwargs.pop("remote"): print("In read only mode, you can't modify the data in the database") diff --git a/abcd/frontends/commandline/parser.py b/abcd/frontends/commandline/parser.py index 9b2c1af2..e3f4b1f1 100644 --- a/abcd/frontends/commandline/parser.py +++ b/abcd/frontends/commandline/parser.py @@ -36,6 +36,11 @@ help="url of abcd api (default: http://localhost)", default="http://localhost", ) +login_parser.add_argument( + "--disable_ssl", + action="store_true", + help="Disable SSL encryption", +) download_parser = subparsers.add_parser( "download", help="download data from the database" @@ -198,6 +203,9 @@ "-u", "--url", help="Url to run the server.", default="http://localhost:5000" ) +refresh_parser = subparsers.add_parser("refresh", help="refresh database") +refresh_parser.set_defaults(callback_func=commands.refresh) + def main(args=None): kwargs = parser.parse_args(args).__dict__ diff --git a/abcd/model.py b/abcd/model.py index f4c87b61..18c47456 100644 --- a/abcd/model.py +++ b/abcd/model.py @@ -4,6 +4,7 @@ from hashlib import md5 from collections import Counter, UserDict from ase.calculators.singlepoint import SinglePointCalculator +from ase.spacegroup.spacegroup import Spacegroup import numpy as np from ase import Atoms @@ -16,7 +17,6 @@ def __init__(self, method=md5()): self.method = method def update(self, value): - if isinstance(value, int): self.update(str(value).encode("ascii")) @@ -44,7 +44,7 @@ def update(self, value): else: raise ValueError( - "The {} type cannot be hashed! (Value: {})", format(type(value), value) + f"The {type(value)} type cannot be hashed! (Value: {value})" ) def __call__(self): @@ -80,14 +80,12 @@ def derived(self): } def __getitem__(self, key): - if key == "derived": return self.derived return super().__getitem__(key) def __setitem__(self, key, value): - if key == "derived": # raise KeyError('Please do not use "derived" as key because it is protected!') # Silent return to avoid raising error in pymongo package @@ -107,7 +105,6 @@ def convert(self, value): return value def update_key_category(self, key, value): - if key == "_id": # raise KeyError('Please do not use "derived" as key because it is protected!') return @@ -190,6 +187,8 @@ def from_atoms(cls, atoms: Atoms, extra_info=None, store_calc=True): for key, value in atoms.info.items(): if isinstance(value, np.ndarray): dct[key] = value.tolist() + elif isinstance(value, Spacegroup): + dct[key] = value.todict() else: dct[key] = value @@ -199,7 +198,6 @@ def from_atoms(cls, atoms: Atoms, extra_info=None, store_calc=True): info_keys.update({"calculator_name", "calculator_parameters"}) for key, value in atoms.calc.results.items(): - if isinstance(value, np.ndarray): if value.shape[0] == n_atoms: arrays_keys.update(key) diff --git a/abcd/parsers/extras.py b/abcd/parsers/extras.py index c007acf6..31cbc069 100644 --- a/abcd/parsers/extras.py +++ b/abcd/parsers/extras.py @@ -6,7 +6,7 @@ start: ( key | key_value )* key: NAME - key_value: NAME "=" value + key_value: NAME ("="|":") value NAME: ("_"|LETTER|DIGIT) ("_"|"-"|LETTER|DIGIT)* @@ -97,7 +97,7 @@ def string(self, s): if __name__ == "__main__": test_string = " ".join( [ - " " "flag", # start with a separator + " flag", # start with a separator 'quotedd_string="quoteddd value"', r'quotedddd_string_escaped="esc\"aped"', "false_value = F", @@ -108,8 +108,9 @@ def string(self, s): "scientific_float_2=5e-6", 'scientific_float_array="1.2 2.2e3 4e1 3.3e-1 2e-2"', 'not_array="1.2 3.4 text"', - "array_nested=[[1,2],[3,4]] " # gets flattented if not 3x3 - "array_many_other_quotes=({[4 8 12]})", + ( # gets flattented if not 3x3 + "array_nested=[[1,2],[3,4]] array_many_other_quotes=({[4 8 12]})" + ), "array_boolean={T F T F}", 'array_boolean_2=" T, F, T " ' # leading spaces # 'not_bool_array=[T F S]', diff --git a/abcd/server/app/db.py b/abcd/server/app/db.py index 65bc9c70..b61a6a04 100644 --- a/abcd/server/app/db.py +++ b/abcd/server/app/db.py @@ -5,7 +5,9 @@ class Database(ABCD): - """Wrapper for the ABCD factory method for registering a the database for the Flask application.""" + """ + Wrapper for the ABCD factory method for registering a the database for the Flask application. + """ def __init__(self): super().__init__() diff --git a/abcd/server/app/nav.py b/abcd/server/app/nav.py index ec8ea2d1..354714cf 100644 --- a/abcd/server/app/nav.py +++ b/abcd/server/app/nav.py @@ -164,7 +164,6 @@ def visit_View(self, node): return item def visit_Subgroup(self, node): - if self._in_dropdown: raise RuntimeError("Cannot render nested Subgroups") diff --git a/abcd/server/app/views/database.py b/abcd/server/app/views/database.py index 8569dcd2..432efafc 100644 --- a/abcd/server/app/views/database.py +++ b/abcd/server/app/views/database.py @@ -21,7 +21,10 @@ def database(database_name): info = { "name": database_name, - "description": "Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus.", + "description": ( + "Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor." + " Duis mollis, est non commodo luctus." + ), "columns": [ {"slug": "formula", "name": "Formula"}, {"slug": "energy", "name": "Energy"}, @@ -43,7 +46,10 @@ def database(database_name): def settings(database_name): info = { "name": database_name, - "description": "Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus.", + "description": ( + "Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor." + " Duis mollis, est non commodo luctus." + ), "columns": [ {"slug": "formula", "name": "Formula"}, {"slug": "energy", "name": "Energy"}, diff --git a/pyproject.toml b/pyproject.toml index b2d5c162..2c299702 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,26 +10,33 @@ repository = "https://github.com/libatoms/abcd" documentation = "https://libatoms.github.io/abcd/" [tool.poetry.dependencies] -python = "^3.9" +ase = "3.22.1" +chardet = "^5.2.0" +lark = "^1.1.9" +matplotlib = "^3.9" notebook = "^7.2" numpy = "^1.26" -tqdm = "^4.66" +openpyxl = "^3.1.2" +opensearch-py = "^2.4.0" +pandas = "^2.2" pymongo = "^4.7.3" -matplotlib = "^3.9" -ase = "3.22.1" -lark = "^1.1.9" +python = "^3.9" +tqdm = "^4.66" [tool.poetry.group.dev.dependencies] +black = "^22.3.0" +flake8 = "^3.7.9" mongomock = "^4.1.2" +openmock = "^2.2" pytest = "^8.2.2" pytest-cov = "^5.0.0" [tool.poetry.extras] -tests = ["mongomock", "pytest", "pytest-cov"] -mongo = ["pymongo"] http = ["requests"] +mongo = ["pymongo"] server-api = ["flask"] server-app = ["flask", "Flask-Nav", "Flask-MongoEngine", "gunicorn", "flask-paginate"] +tests = ["mongomock", "pytest", "pytest-cov"] [build-system] requires = ["poetry-core"] @@ -37,3 +44,6 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."console_scripts"] "abcd" = "abcd.frontends.commandline:main" + +[tool.black] +line-length = 88 diff --git a/tests/data/example.xyz b/tests/data/example.xyz new file mode 100644 index 00000000..2a81c26d --- /dev/null +++ b/tests/data/example.xyz @@ -0,0 +1,4 @@ +2 +Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" energy=-101.9 +Si 0.00000000 1.00000000 2.00000000 +Si 4.00000000 5.00000000 6.00000000 diff --git a/tests/data/example_2.xyz b/tests/data/example_2.xyz new file mode 100644 index 00000000..13315d57 --- /dev/null +++ b/tests/data/example_2.xyz @@ -0,0 +1,5 @@ +3 +Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" energy=-99.33 +Si 0.00000000 1.00000000 2.00000000 +Si 3.00000000 4.00000000 5.00000000 +Si 6.00000000 7.00000000 7.00000000 diff --git a/tests/data/examples.csv b/tests/data/examples.csv new file mode 100755 index 00000000..6473a822 --- /dev/null +++ b/tests/data/examples.csv @@ -0,0 +1,4 @@ +Text,Integers,Floating,Boolean,Missing data,"Comma units, m",Bracket units (s) +Some,1,0.01,TRUE,Missing,0,0 +test,2,0.1,FALSE,,1,1 +data,3,1,FALSE,data,2,2 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..3089affc --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,125 @@ +import logging +import os +from pathlib import Path +import subprocess +from time import sleep + +import pytest + + +DATA_PATH = Path(__file__).parent / "data" + +NOT_GTHUB_ACTIONS = True +if os.getenv("GITHUB_ACTIONS") == "true": + NOT_GTHUB_ACTIONS = False + + +@pytest.mark.skipif(NOT_GTHUB_ACTIONS, reason="Not running via GitHub Actions") +class TestCli: + """Testing OpenSearch database CLI integration.""" + + @pytest.fixture(autouse=True) + def abcd(self): + """Set up OpenSearch database connection and login with CLI.""" + security_enabled = os.getenv("security_enabled") == "true" + port = int(os.environ["port"]) + host = "localhost" + if os.environ["opensearch-version"] == "latest": + credential = "admin:myStrongPassword123!" + else: + credential = "admin:admin" + + logging.basicConfig(level=logging.INFO) + + url = f"opensearch://{credential}@{host}:{port}" + if not security_enabled: + url += " --disable_ssl" + try: + subprocess.run(f"abcd login {url}", shell=True, check=True) + except subprocess.CalledProcessError: + sleep(10) + subprocess.run(f"abcd login {url}", shell=True, check=True) + + def test_summary(self, abcd): + """ + Test summary output of uploaded data file. + """ + data_file = DATA_PATH / "example.xyz" + + subprocess.run( + f"abcd upload {data_file} -i -e 'test_data'", shell=True, check=True + ) + subprocess.run(f"abcd refresh", shell=True, check=True) + + summary = subprocess.run( + "abcd summary", shell=True, check=True, capture_output=True, text=True + ) + assert "Total number of configurations" in summary.stdout + subprocess.run(f"abcd delete -q 'test_data' -y", shell=True) + + def test_query(self, abcd): + """ + Test lucene-style query. + """ + data_file_1 = DATA_PATH / "example.xyz" + data_file_2 = DATA_PATH / "example_2.xyz" + + subprocess.run( + f"abcd upload {data_file_1} -i -e 'test_data'", shell=True, check=True + ) + subprocess.run( + f"abcd upload {data_file_2} -i -e 'test_data'", shell=True, check=True + ) + subprocess.run(f"abcd refresh", shell=True, check=True) + + summary = subprocess.run( + "abcd show -p n_atoms -q 'n_atoms : 2'", + shell=True, + check=True, + capture_output=True, + text=True, + ) + assert "2" in summary.stdout and "3" not in summary.stdout + summary = subprocess.run( + "abcd show -p n_atoms -q 'n_atoms : 3'", + shell=True, + check=True, + capture_output=True, + text=True, + ) + assert "3" in summary.stdout and "2" not in summary.stdout + subprocess.run(f"abcd delete -q 'test_data' -y", shell=True) + + def test_range_query(self, abcd): + """ + Test lucene-style ranged query. + """ + data_file_1 = DATA_PATH / "example.xyz" + data_file_2 = DATA_PATH / "example_2.xyz" + + subprocess.run( + f"abcd upload {data_file_1} -i -e 'test_data'", shell=True, check=True + ) + subprocess.run( + f"abcd upload {data_file_2} -i -e 'test_data'", shell=True, check=True + ) + subprocess.run(f"abcd refresh", shell=True, check=True) + + summary = subprocess.run( + "abcd summary -p energy -q 'energy:[-100 TO -99]'", + shell=True, + check=True, + capture_output=True, + text=True, + ) + assert "Total number of configurations: 1" in summary.stdout + + summary = subprocess.run( + "abcd summary -p energy -q 'energy:[-102 TO -99]'", + shell=True, + check=True, + capture_output=True, + text=True, + ) + assert "Total number of configurations: 2" in summary.stdout + subprocess.run(f"abcd delete -q 'test_data' -y", shell=True) diff --git a/tests/test_mongomock.py b/tests/test_mongomock.py new file mode 100644 index 00000000..5444fe59 --- /dev/null +++ b/tests/test_mongomock.py @@ -0,0 +1,44 @@ +from io import StringIO +import logging +import unittest + +from ase.io import read +from ase.atoms import Atoms +import mongomock +import pytest + +from abcd import ABCD + + +class TestMongoMock: + @pytest.fixture(autouse=True) + @mongomock.patch(servers=(("localhost", 27017),)) + def abcd(self): + logging.basicConfig(level=logging.INFO) + url = "mongodb://localhost" + mongo_abcd = ABCD.from_url(url) + mongo_abcd.print_info() + return mongo_abcd + + def test_info(self, abcd): + print(abcd.info()) + + def test_push(self, abcd): + xyz = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms = read(xyz, format="extxyz") + assert isinstance(atoms, Atoms) + atoms.set_cell([1, 1, 1]) + + abcd.destroy() + abcd.push(atoms) + new = list(abcd.get_atoms())[0] + + assert atoms == new + abcd.destroy() diff --git a/tests/test_opensearch.py b/tests/test_opensearch.py new file mode 100644 index 00000000..1735bca9 --- /dev/null +++ b/tests/test_opensearch.py @@ -0,0 +1,533 @@ +from io import StringIO +import logging +import os +from time import sleep + +from ase.atoms import Atoms +from ase.io import read +from opensearchpy.exceptions import ConnectionError +import pytest + +from abcd import ABCD +from abcd.backends.atoms_opensearch import AtomsModel, OpenSearchDatabase + +NOT_GTHUB_ACTIONS = True +if os.getenv("GITHUB_ACTIONS") == "true": + NOT_GTHUB_ACTIONS = False + + +@pytest.mark.skipif(NOT_GTHUB_ACTIONS, reason="Not running via GitHub Actions") +class TestOpenSearch: + """Testing live OpenSearch database functions.""" + + @pytest.fixture(autouse=True) + def abcd(self): + """Set up OpenSearch database connection.""" + security_enabled = os.getenv("security_enabled") == "true" + self.port = int(os.environ["port"]) + self.host = "localhost" + if os.environ["opensearch-version"] == "latest": + credential = "admin:myStrongPassword123!" + else: + credential = "admin:admin" + + logging.basicConfig(level=logging.INFO) + + url = f"opensearch://{credential}@{self.host}:{self.port}" + try: + abcd_opensearch = ABCD.from_url( + url, + index_name="test_index", + use_ssl=security_enabled, + ) + except (ConnectionError, ConnectionResetError): + sleep(10) + abcd_opensearch = ABCD.from_url( + url, + index_name="test_index", + use_ssl=security_enabled, + ) + + assert isinstance(abcd_opensearch, OpenSearchDatabase) + return abcd_opensearch + + def push_data(self, abcd): + """Helper function to upload an example xyz file to the database.""" + xyz = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms = read(xyz, format="extxyz") + assert isinstance(atoms, Atoms) + atoms.set_cell([1, 1, 1]) + abcd.push(atoms) + abcd.refresh() + + def test_info(self, abcd): + """Test printing database info.""" + abcd.destroy() + abcd.create() + abcd.refresh() + abcd.print_info() + + info = { + "host": self.host, + "port": self.port, + "db": "abcd", + "index": "test_index", + "number of confs": 0, + "type": "opensearch", + } + assert abcd.info() == info + + def test_destroy(self, abcd): + """Test destroying database index.""" + abcd.destroy() + abcd.create() + abcd.refresh() + assert abcd.client.indices.exists("test_index") is True + + abcd.destroy() + assert abcd.client.indices.exists("test_index") is False + + def test_create(self, abcd): + """Test creating database index.""" + abcd.destroy() + abcd.create() + abcd.refresh() + assert abcd.client.indices.exists("test_index") is True + assert abcd.client.indices.exists("fake_index") is False + + def test_push(self, abcd): + """Test pushing atoms objects to database individually.""" + abcd.destroy() + abcd.create() + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1) + + xyz_2 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + W 0.00000000 0.00000000 0.00000000 + W 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + + abcd.refresh() + result = AtomsModel( + None, + None, + abcd.client.search(index="test_index")["hits"]["hits"][0]["_source"], + ).to_ase() + assert atoms_1 == result + assert atoms_2 != result + + def test_delete(self, abcd): + """Test deleting all documents from database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + self.push_data(abcd) + abcd.refresh() + + assert abcd.count() == 2 + abcd.delete() + assert abcd.client.indices.exists("test_index") is True + abcd.refresh() + assert abcd.count() == 0 + + def test_bulk(self, abcd): + """Test pushing atoms object to database together.""" + abcd.destroy() + abcd.create() + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + + xyz_2 = StringIO( + """1 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + + atoms_list = [] + atoms_list.append(atoms_1) + atoms_list.append(atoms_2) + abcd.push(atoms_list) + + abcd.refresh() + assert abcd.count() == 2 + result_1 = AtomsModel( + None, + None, + abcd.client.search(index="test_index")["hits"]["hits"][0]["_source"], + ).to_ase() + result_2 = AtomsModel( + None, + None, + abcd.client.search(index="test_index")["hits"]["hits"][1]["_source"], + ).to_ase() + assert atoms_1 == result_1 + assert atoms_2 == result_2 + + def test_count(self, abcd): + """Test counting the number of documents in the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + self.push_data(abcd) + assert abcd.count() == 2 + + def test_property(self, abcd): + """Test getting values of a property from the database.""" + abcd.destroy() + abcd.create() + + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 energy=-5.0 prop_1="test_1" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1, store_calc=False) + + xyz_2 = StringIO( + """2 + Properties=species:S:1:pos:R:3 energy=-10.0 prop_2="test_2" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + abcd.push(atoms_2, store_calc=False) + + abcd.refresh() + prop = abcd.property("prop_1") + expected_prop = ["test_1"] + assert prop == expected_prop + + prop = abcd.property("energy") + expected_prop = [-5.0, -10.0] + assert prop[0] == expected_prop[0] + assert prop[1] == expected_prop[1] + + def test_properties(self, abcd): + """Test getting all properties from the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + props = abcd.properties() + expected_props = { + "info": ["_vtk_test", "cell", "formula", "n_atoms", "pbc", "s", "volume"], + "derived": [ + "elements", + "hash", + "hash_structure", + "modified", + "uploaded", + "username", + "volume", + ], + "arrays": ["numbers", "positions"], + } + assert props == expected_props + + def test_count_property(self, abcd): + """Test counting values of specified properties from the database.""" + abcd.destroy() + abcd.create() + + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" prop_1="1" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1) + + xyz_2 = StringIO( + """1 + Properties=species:S:1:pos:R:3 s="sadf" prop_2="2" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + abcd.push(atoms_2) + + abcd.refresh() + assert abcd.count_property("prop_1") == {1: 1} + assert abcd.count_property("n_atoms") == {1: 1, 2: 1} + assert abcd.count_property("volume") == {1.0: 2} + + def test_count_properties(self, abcd): + """Test counting appearences of each property in documents in the database.""" + abcd.destroy() + abcd.create() + + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" prop_1="test_1" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1) + + xyz_2 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" prop_2="test_2" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + abcd.push(atoms_2) + + abcd.refresh() + props = abcd.count_properties() + expected_counts = { + "prop_1": {"count": 1, "category": "info", "dtype": "scalar(str)"}, + "prop_2": {"count": 1, "category": "info", "dtype": "scalar(str)"}, + "cell": {"count": 2, "category": "info", "dtype": "array(float)"}, + "elements": {"count": 2, "category": "derived", "dtype": "scalar(dict)"}, + "formula": {"count": 2, "category": "info", "dtype": "scalar(str)"}, + "hash": {"count": 2, "category": "derived", "dtype": "scalar(str)"}, + "hash_structure": { + "count": 2, + "category": "derived", + "dtype": "scalar(str)", + }, + "modified": {"count": 2, "category": "derived", "dtype": "scalar(str)"}, + "n_atoms": {"count": 2, "category": "info", "dtype": "scalar(int)"}, + "numbers": {"count": 2, "category": "arrays", "dtype": "vector(int, N)"}, + "pbc": {"count": 2, "category": "info", "dtype": "vector(bool)"}, + "positions": { + "count": 2, + "category": "arrays", + "dtype": "array(float, N x 3)", + }, + "s": {"count": 2, "category": "info", "dtype": "scalar(str)"}, + "uploaded": {"count": 2, "category": "derived", "dtype": "scalar(str)"}, + "username": {"count": 2, "category": "derived", "dtype": "scalar(str)"}, + "volume": {"count": 2, "category": "derived", "dtype": "scalar(float)"}, + } + + assert props == expected_counts + + def test_add_property(self, abcd): + """Test adding a property to documents in the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + abcd.add_property({"TEST_PROPERTY": "TEST_VALUE"}) + + abcd.refresh() + data = abcd.client.search(index="test_index") + assert data["hits"]["hits"][0]["_source"]["TEST_PROPERTY"] == "TEST_VALUE" + assert ( + "TEST_PROPERTY" + in data["hits"]["hits"][0]["_source"]["derived"]["info_keys"] + ) + + def test_rename_property(self, abcd): + """Test renaming a property for documents in the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + abcd.add_property({"TEST_PROPERTY": "TEST_VALUE"}) + abcd.refresh() + abcd.rename_property("TEST_PROPERTY", "NEW_PROPERTY") + abcd.refresh() + + data = abcd.client.search(index="test_index") + assert data["hits"]["hits"][0]["_source"]["NEW_PROPERTY"] == "TEST_VALUE" + + def test_delete_property(self, abcd): + """Test deleting a property from documents in the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + + abcd.add_property({"TEST_PROPERTY": "TEST_VALUE"}) + abcd.refresh() + data = abcd.client.search(index="test_index") + assert data["hits"]["hits"][0]["_source"]["TEST_PROPERTY"] == "TEST_VALUE" + + abcd.delete_property("TEST_PROPERTY") + abcd.refresh() + data = abcd.client.search(index="test_index") + with pytest.raises(KeyError): + data["hits"]["hits"][0]["_source"]["TEST_PROPERTY"] + assert ( + "TEST_PROPERTY" + not in data["hits"]["hits"][0]["_source"]["derived"]["info_keys"] + ) + + def test_get_items(self, abcd): + """Test getting a dictionary of values from documents in the database.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + + expected_items = { + "_id": None, + "n_atoms": 2, + "numbers": [14, 14], + "_vtk_test": "t _ e s t", + "positions": [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], + "cell": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + "pbc": [False, False, False], + "volume": 1.0, + "hash_structure": None, + "s": "sadf", + "elements": {"14": 2}, + "uploaded": None, + "formula": "Si2", + "modified": None, + "derived": { + "info_keys": [ + "s", + "n_atoms", + "_vtk_test", + "cell", + "pbc", + "formula", + "volume", + ], + "derived_keys": [ + "elements", + "username", + "uploaded", + "modified", + "volume", + "hash_structure", + "hash", + ], + "arrays_keys": ["numbers", "positions"], + "results_keys": [], + }, + "hash": None, + "username": None, + } + + abcd.refresh() + items = list(abcd.get_items())[0] + + for key in expected_items: + if key not in [ + "_id", + "hash_structure", + "uploaded", + "modified", + "hash", + "username", + ]: + if isinstance(expected_items[key], dict): + for dict_key in expected_items[key]: + if isinstance(expected_items[key][dict_key], list): + assert set(expected_items[key][dict_key]) == set( + items[key][dict_key] + ) + else: + assert expected_items[key][dict_key] == items[key][dict_key] + else: + assert expected_items[key] == items[key] + + def test_get_atoms(self, abcd): + """Test getting values from documents in the database as Atoms objects.""" + abcd.destroy() + abcd.create() + self.push_data(abcd) + expected_atoms = Atoms(symbols="Si2", pbc=False, cell=[1.0, 1.0, 1.0]) + assert expected_atoms == list(abcd.get_atoms())[0] + + def test_query(self, abcd): + """Test querying documents in the database.""" + abcd.destroy() + abcd.create() + + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" prop_1="test_1" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1) + + xyz_2 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" prop_2="test_2" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + abcd.push(atoms_2) + abcd.refresh() + + query_dict = {"match": {"n_atoms": 2}} + query_all = "volume: [0 TO 10]" + query_1 = "prop_1: *" + query_2 = "prop_2: *" + assert abcd.count(query_dict) == 2 + assert abcd.count(query_all) == 2 + assert abcd.count(query_1) == 1 + assert abcd.count(query_2) == 1 diff --git a/tests/test_opensearch_mock.py b/tests/test_opensearch_mock.py new file mode 100644 index 00000000..94dd6711 --- /dev/null +++ b/tests/test_opensearch_mock.py @@ -0,0 +1,160 @@ +from io import StringIO +import logging +import os + +from ase.atoms import Atoms +from ase.io import read +from openmock import openmock +import pytest + +from abcd import ABCD +from abcd.backends.atoms_opensearch import AtomsModel, OpenSearchDatabase + + +class TestOpenSearchMock: + """Testing mock OpenSearch database functions.""" + + @pytest.fixture(autouse=True) + @openmock + def abcd(self): + """Set up database connection.""" + + if "port" in os.environ: + port = int(os.environ["port"]) + else: + port = 9200 + host = "localhost" + security_enabled = os.getenv("security_enabled") == "true" + if os.environ["opensearch-version"] == "latest": + credential = "admin:myStrongPassword123!" + else: + credential = "admin:admin" + + logging.basicConfig(level=logging.INFO) + + url = f"opensearch://{credential}@{host}:{port}" + opensearch_abcd = ABCD.from_url( + url, + index_name="test_index", + use_ssl=security_enabled, + ) + assert isinstance(opensearch_abcd, OpenSearchDatabase) + return opensearch_abcd + + def test_destroy(self, abcd): + """ + Test destroying database index. + """ + assert abcd.client.indices.exists("test_index") is True + abcd.destroy() + assert abcd.client.indices.exists("test_index") is False + + def test_create(self, abcd): + """ + Test creating database index. + """ + abcd.destroy() + abcd.create() + assert abcd.client.indices.exists("test_index") is True + abcd.client.indices.exists("fake_index") is False + + def test_push(self, abcd): + """ + Test pushing atoms objects to database individually. + """ + abcd.destroy() + abcd.create() + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + abcd.push(atoms_1) + + xyz_2 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + W 0.00000000 0.00000000 0.00000000 + W 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + + abcd.refresh() + result = AtomsModel( + dict=abcd.client.search(index="test_index")["hits"]["hits"][0]["_source"], + ).to_ase() + assert atoms_1 == result + assert atoms_2 != result + + def test_bulk(self, abcd): + """ + Test pushing atoms object to database together. + """ + abcd.destroy() + abcd.create() + xyz_1 = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_1 = read(xyz_1, format="extxyz") + assert isinstance(atoms_1, Atoms) + atoms_1.set_cell([1, 1, 1]) + + xyz_2 = StringIO( + """1 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + """ + ) + atoms_2 = read(xyz_2, format="extxyz") + assert isinstance(atoms_2, Atoms) + atoms_2.set_cell([1, 1, 1]) + + atoms_list = [] + atoms_list.append(atoms_1) + atoms_list.append(atoms_2) + abcd.push(atoms_list) + abcd.refresh() + assert abcd.count() == 2 + + result_1 = AtomsModel( + dict=abcd.client.search(index="test_index")["hits"]["hits"][0]["_source"], + ).to_ase() + result_2 = AtomsModel( + dict=abcd.client.search(index="test_index")["hits"]["hits"][1]["_source"], + ).to_ase() + assert atoms_1 == result_1 + assert atoms_2 == result_2 + + def test_count(self, abcd): + """ + Test counting the number of documents in the database. + """ + abcd.destroy() + abcd.create() + xyz = StringIO( + """2 + Properties=species:S:1:pos:R:3 s="sadf" _vtk_test="t _ e s t" pbc="F F F" + Si 0.00000000 0.00000000 0.00000000 + Si 0.00000000 0.00000000 0.00000000 + """ + ) + + atoms = read(xyz, format="extxyz") + assert isinstance(atoms, Atoms) + atoms.set_cell([1, 1, 1]) + abcd.push(atoms) + abcd.push(atoms) + abcd.refresh() + assert abcd.count() == 2 diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 5a2211e0..708809e1 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -52,6 +52,7 @@ def test_string(self, parser, string, expected): ("true_value", {"true_value": True}), ("true_value_long = true", {"true_value_long": True}), ("false_value = F", {"false_value": False}), + ("false_value_colon: F", {"false_value_colon": False}), ], ) def test_boolean(self, parser, string, expected): @@ -65,6 +66,7 @@ def test_boolean(self, parser, string, expected): ("floating=1.1", {"floating": 1.1}), ("scientific_float=1.2e7", {"scientific_float": 1.2e7}), ("scientific_float_2=5e-6", {"scientific_float_2": 5e-6}), + ("floating_colon: 3.14", {"floating_colon": 3.14}), ], ) def test_numbers(self, parser, string, expected): @@ -86,6 +88,7 @@ def test_numbers(self, parser, string, expected): "array_bool_commas=[T, T, F, T]", {"array_bool_commas": [True, True, False, True]}, ), + ("int_array_colon: {4 2}", {"int_array_colon": [4, 2]}), ], ) def test_arrays(self, parser, string, expected): @@ -124,6 +127,17 @@ def test_composite(self, parser): out = parser.parse(composite_string) assert out == composite_expected + @pytest.mark.parametrize( + "string, expected", + [ + ('colon_string:"astring"', {"colon_string": "astring"}), + ('colon_string_spaces : "astring"', {"colon_string_spaces": "astring"}), + ], + ) + def test_colon_key_value_pairs(self, parser, string, expected): + """Key value pairs separated by colons""" + assert expected == parser.parse(string) + @pytest.mark.skip @pytest.mark.parametrize( "string", diff --git a/tests/test_properties.py b/tests/test_properties.py new file mode 100644 index 00000000..1578fc64 --- /dev/null +++ b/tests/test_properties.py @@ -0,0 +1,155 @@ +import os + +from pandas import DataFrame +import pytest + +from abcd.backends.atoms_properties import Properties + + +class TestProperties: + """Testing properties data reader""" + + @pytest.fixture(autouse=True) + def property(self): + """Load example data file.""" + class_path = os.path.normpath(os.path.abspath(__file__)) + data_file = os.path.dirname(class_path) + "/data/examples.csv" + return Properties(data_file) + + def test_dataframe(self, property): + """ + Test data correctly stored in pandas DataFrame. + """ + assert isinstance(property.df, DataFrame) + assert len(property.df) == 3 + + def test_specify_units(self, property): + """ + Test units can be specified manually, if they match existing fields. + """ + input_units_1 = {"Integers": "items", "Floating": "seconds"} + properties_1 = Properties( + data_file=property.data_file, + units=input_units_1, + ) + assert properties_1.units == input_units_1 + + input_units_2 = {"Fake": "m"} + with pytest.raises(ValueError): + properties_1 = Properties( + data_file=property.data_file, + units=input_units_2, + ) + + def test_infer_units(self, property): + """ + Test units can be inferred from field names. + """ + properties = Properties( + data_file=property.data_file, + infer_units=True, + ) + expected_units = {"Comma units": "m", "Bracket units": "s"} + expected_fields = [ + "Text", + "Integers", + "Floating", + "Boolean", + "Missing data", + "Comma units", + "Bracket units", + ] + assert properties.units == expected_units + assert list(properties.df.columns.values) == expected_fields + + def test_struct_file(self, property): + """ + Test structure file names can be inferred from a field. + """ + struct_file_template = "test_{struct_name}_file.txt" + struct_name_label = "Text" + properties_1 = Properties( + data_file=property.data_file, + store_struct_file=True, + struct_file_template=struct_file_template, + struct_name_label=struct_name_label, + ) + expected_struct_files = [ + "test_Some_file.txt", + "test_test_file.txt", + "test_data_file.txt", + ] + assert isinstance(properties_1.struct_files, list) + for i, file in enumerate(expected_struct_files): + assert properties_1.struct_files[i] == file + + invalid_template = "invalid_template" + with pytest.raises(ValueError): + Properties( + data_file=property.data_file, + store_struct_file=True, + struct_file_template=invalid_template, + struct_name_label=struct_name_label, + ) + + invalid_label = "label" + with pytest.raises(ValueError): + Properties( + data_file=property.data_file, + store_struct_file=True, + struct_file_template=struct_file_template, + struct_name_label=invalid_label, + ) + + def test_to_list(self, property): + """ + Test dataframe can be converted into a list of properties. + """ + assert len(property.to_list()) == 3 + assert isinstance(property.to_list(), list) + assert isinstance(property.to_list()[0], dict) + expected_property = { + "Text": "Some", + "Integers": 1, + "Floating": 0.01, + "Boolean": True, + "Missing data": "Missing", + "Comma units, m": 0, + "Bracket units (s)": 0, + } + assert property.to_list()[0] == expected_property + + def test_missing_data(self, property): + """ + Test missing data is not included in properties. + """ + expected_property = { + "Text": "test", + "Integers": 2, + "Floating": 0.1, + "Boolean": False, + "Comma units, m": 1, + "Bracket units (s)": 1, + } + assert property.to_list()[1] == expected_property + + def test_to_list_units(self, property): + """ + Test units are included in properties when converting to a list. + """ + properties_1 = Properties( + data_file=property.data_file, + infer_units=True, + ) + expected_units = {"Comma units": "m", "Bracket units": "s"} + expected_property = { + "Text": "Some", + "Integers": 1, + "Floating": 0.01, + "Boolean": True, + "Missing data": "Missing", + "Comma units": 0, + "Bracket units": 0, + "units": expected_units, + } + assert properties_1.to_list()[0] == expected_property diff --git a/tutorials/abcd_benchmarking.ipynb b/tutorials/abcd_benchmarking.ipynb new file mode 100644 index 00000000..aa0fdc3f --- /dev/null +++ b/tutorials/abcd_benchmarking.ipynb @@ -0,0 +1,1147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Benchmarking OpenSearch performance against MongoDB " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from abcd import ABCD" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenSearchDatabase(url=localhost:9200, index=atoms) \n" + ] + } + ], + "source": [ + "os_url = 'opensearch://admin:admin@localhost:9200'\n", + "os_abcd = ABCD.from_url(os_url)\n", + "\n", + "print(os_abcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MongoDatabase(url=localhost:27017, db=abcd, collection=atoms)\n" + ] + } + ], + "source": [ + "mongo_url = 'mongodb://localhost:27017'\n", + "mongo_abcd = ABCD.from_url(mongo_url)\n", + "\n", + "print(mongo_abcd)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Data in each database consists of ~400,000 structures, made up of 2055 structures repeated 192 times. Of these, half were uploaded only as atoms objects, and half were uploaded with extra information added from a csv file." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 394560\n", + " type: opensearch\n", + "CPU times: user 0 ns, sys: 2.34 ms, total: 2.34 ms\n", + "Wall time: 7.86 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "os_abcd.print_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================== ABCD MongoDB ==================\n", + " type: mongodb\n", + " host: localhost\n", + " port: 27017\n", + " db: abcd\n", + "collection: atoms\n", + "number of confs: 394560\n", + " type: mongodb\n", + "CPU times: user 0 ns, sys: 2.73 ms, total: 2.73 ms\n", + "Wall time: 323 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "mongo_abcd.print_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 67.1 ms, sys: 8.17 ms, total: 75.3 ms\n", + "Wall time: 207 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "{'info': ['1aromatico-up',\n", + " '2D',\n", + " '2aromatici-up',\n", + " '5-m-rings',\n", + " '5m-ring-leg2met',\n", + " '6m-rings',\n", + " 'Accessible Surface Area',\n", + " 'Band_gap',\n", + " 'CN-M',\n", + " 'COOM',\n", + " 'Cell volume',\n", + " 'Crit: metal',\n", + " 'Crit: pi-pi stacking',\n", + " 'Crit: redox active linker',\n", + " 'Crit: redox match',\n", + " 'Criteria#',\n", + " 'Density',\n", + " 'Dos at CBM',\n", + " 'Dos at Fermi energy',\n", + " 'Dos at VBM',\n", + " 'HSE band gap',\n", + " 'LCD',\n", + " 'M-C-C-TRIANG',\n", + " 'M-H2O-M',\n", + " 'M-N-NM-N-M',\n", + " 'M-h2o',\n", + " 'MOF_name',\n", + " 'Metal',\n", + " 'Metal 2',\n", + " 'Metal 3',\n", + " 'Metal density',\n", + " 'Metals number',\n", + " 'Multiplier_Sum',\n", + " 'N3--NCN up',\n", + " 'PLD',\n", + " 'Space_group',\n", + " 'Space_group#',\n", + " 'Temp',\n", + " 'Volume Fraction',\n", + " 'Year',\n", + " 'Zprime',\n", + " 'author',\n", + " 'benzene',\n", + " 'cell',\n", + " 'energy',\n", + " 'formula',\n", + " 'metal-N',\n", + " 'metal-O',\n", + " 'metal-S',\n", + " 'metal-halogen',\n", + " 'n_atoms',\n", + " 'pbc',\n", + " 'pyridine',\n", + " 'pyrimidine',\n", + " 'units',\n", + " 'volume',\n", + " 'without ions'],\n", + " 'derived': ['elements',\n", + " 'hash',\n", + " 'hash_structure',\n", + " 'modified',\n", + " 'uploaded',\n", + " 'username',\n", + " 'volume'],\n", + " 'arrays': ['forces', 'numbers', 'positions']}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "os_abcd.properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 4.59 ms, sys: 3.13 ms, total: 7.72 ms\n", + "Wall time: 6.13 s\n" + ] + }, + { + "data": { + "text/plain": [ + "{'info': ['Dos at Fermi energy',\n", + " 'pbc',\n", + " 'Metal 3',\n", + " 'Multiplier_Sum',\n", + " '1aromatico-up',\n", + " 'Crit: redox match',\n", + " 'volume',\n", + " 'without ions',\n", + " 'Density',\n", + " 'metal-halogen',\n", + " 'MOF_name',\n", + " 'pyrimidine',\n", + " 'Dos at VBM',\n", + " 'COOM',\n", + " 'Metals number',\n", + " 'Crit: redox active linker',\n", + " 'Accessible Surface Area',\n", + " 'Metal density',\n", + " 'M-C-C-TRIANG',\n", + " 'HSE band gap',\n", + " 'metal-O',\n", + " 'M-h2o',\n", + " 'Dos at CBM',\n", + " 'PLD',\n", + " 'metal-S',\n", + " '2D',\n", + " 'energy',\n", + " 'Band_gap',\n", + " 'M-N-NM-N-M',\n", + " 'N3--NCN up',\n", + " 'Space_group',\n", + " 'cell',\n", + " 'Crit: pi-pi stacking',\n", + " '5m-ring-leg2met',\n", + " 'LCD',\n", + " 'Volume Fraction',\n", + " 'Criteria#',\n", + " 'formula',\n", + " 'Zprime',\n", + " 'Crit: metal',\n", + " '5-m-rings',\n", + " 'M-H2O-M',\n", + " 'Cell volume',\n", + " 'Metal 2',\n", + " 'author',\n", + " '2aromatici-up',\n", + " 'benzene',\n", + " 'metal-N',\n", + " 'CN-M',\n", + " '6m-rings',\n", + " 'units',\n", + " 'n_atoms',\n", + " 'Year',\n", + " 'Space_group#',\n", + " 'pyridine',\n", + " 'Temp',\n", + " 'Metal'],\n", + " 'arrays': ['forces', 'numbers', 'positions'],\n", + " 'derived': ['username',\n", + " 'volume',\n", + " 'uploaded',\n", + " 'hash',\n", + " 'modified',\n", + " 'elements',\n", + " 'hash_structure']}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "mongo_abcd.properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 98.5 ms, sys: 22.1 ms, total: 121 ms\n", + "Wall time: 446 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "{'1aromatico-up': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " '2D': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '2aromatici-up': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " '5-m-rings': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '5m-ring-leg2met': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " '6m-rings': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Accessible Surface Area': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Band_gap': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'CN-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'COOM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Cell volume': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Crit: metal': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Crit: pi-pi stacking': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Crit: redox active linker': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Crit: redox match': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Criteria#': {'count': 197280, 'category': 'info', 'dtype': 'scalar(int)'},\n", + " 'Density': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Dos at CBM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'Dos at Fermi energy': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(float)'},\n", + " 'Dos at VBM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'HSE band gap': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'LCD': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-C-C-TRIANG': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-H2O-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-N-NM-N-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-h2o': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'MOF_name': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal 2': {'count': 9034, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal 3': {'count': 409, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal density': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Metals number': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Multiplier_Sum': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'N3--NCN up': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'PLD': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Space_group': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Space_group#': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Temp': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Volume Fraction': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Year': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Zprime': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'author': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'benzene': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'cell': {'count': 394560, 'category': 'info', 'dtype': 'array(float)'},\n", + " 'elements': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(dict)'},\n", + " 'energy': {'count': 394560, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'forces': {'count': 394560,\n", + " 'category': 'arrays',\n", + " 'dtype': 'array(float, N x 3)'},\n", + " 'formula': {'count': 394560, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'hash': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'hash_structure': {'count': 394560,\n", + " 'category': 'derived',\n", + " 'dtype': 'scalar(str)'},\n", + " 'metal-N': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-O': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-S': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-halogen': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'modified': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'n_atoms': {'count': 394560, 'category': 'info', 'dtype': 'scalar(int)'},\n", + " 'numbers': {'count': 394560, 'category': 'arrays', 'dtype': 'vector(int, N)'},\n", + " 'pbc': {'count': 394560, 'category': 'info', 'dtype': 'vector(bool)'},\n", + " 'positions': {'count': 394560,\n", + " 'category': 'arrays',\n", + " 'dtype': 'array(float, N x 3)'},\n", + " 'pyridine': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'pyrimidine': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'units': {'count': 197280, 'category': 'info', 'dtype': 'scalar(dict)'},\n", + " 'uploaded': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'username': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'volume': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(float)'},\n", + " 'without ions': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'}}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "os_abcd.count_properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 78.6 ms, sys: 10 ms, total: 88.6 ms\n", + "Wall time: 21.7 s\n" + ] + }, + { + "data": { + "text/plain": [ + "{'Dos at Fermi energy': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(float)'},\n", + " 'pbc': {'count': 394560, 'category': 'info', 'dtype': 'vector(bool)'},\n", + " 'Metal 3': {'count': 409, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Multiplier_Sum': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " '1aromatico-up': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Crit: redox match': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'volume': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(float)'},\n", + " 'without ions': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Density': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-halogen': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'MOF_name': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'pyrimidine': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Dos at VBM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'COOM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metals number': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Crit: redox active linker': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Accessible Surface Area': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Metal density': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'M-C-C-TRIANG': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'HSE band gap': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-O': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-h2o': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Dos at CBM': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'PLD': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-S': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '2D': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'energy': {'count': 394560, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'Space_group': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-N-NM-N-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'N3--NCN up': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Band_gap': {'count': 197280, 'category': 'info', 'dtype': 'scalar(float)'},\n", + " 'cell': {'count': 394560, 'category': 'info', 'dtype': 'array(float)'},\n", + " 'Crit: pi-pi stacking': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " '5m-ring-leg2met': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'LCD': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Volume Fraction': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'Criteria#': {'count': 197280, 'category': 'info', 'dtype': 'scalar(int)'},\n", + " 'formula': {'count': 394560, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Zprime': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Crit: metal': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '5-m-rings': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'M-H2O-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Cell volume': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal 2': {'count': 9034, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'author': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '2aromatici-up': {'count': 197280,\n", + " 'category': 'info',\n", + " 'dtype': 'scalar(str)'},\n", + " 'benzene': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'metal-N': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'CN-M': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " '6m-rings': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'units': {'count': 197280, 'category': 'info', 'dtype': 'scalar(dict)'},\n", + " 'Year': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'n_atoms': {'count': 394560, 'category': 'info', 'dtype': 'scalar(int)'},\n", + " 'Space_group#': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'pyridine': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Temp': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'Metal': {'count': 197280, 'category': 'info', 'dtype': 'scalar(str)'},\n", + " 'positions': {'count': 394560,\n", + " 'category': 'arrays',\n", + " 'dtype': 'array(float, N x 3)'},\n", + " 'forces': {'count': 394560,\n", + " 'category': 'arrays',\n", + " 'dtype': 'array(float, N x 3)'},\n", + " 'numbers': {'count': 394560, 'category': 'arrays', 'dtype': 'vector(int, N)'},\n", + " 'modified': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(date)'},\n", + " 'uploaded': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(date)'},\n", + " 'hash_structure': {'count': 394560,\n", + " 'category': 'derived',\n", + " 'dtype': 'scalar(str)'},\n", + " 'username': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'hash': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(str)'},\n", + " 'elements': {'count': 394560, 'category': 'derived', 'dtype': 'scalar(dict)'}}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "mongo_abcd.count_properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 3.18 ms, sys: 630 µs, total: 3.81 ms\n", + "Wall time: 8.16 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "{306: 67200,\n", + " 210: 64128,\n", + " 114: 60672,\n", + " 222: 15168,\n", + " 126: 10752,\n", + " 252: 7104,\n", + " 177: 6912,\n", + " 237: 6528,\n", + " 141: 6144,\n", + " 138: 5184,\n", + " 249: 5184,\n", + " 195: 4992,\n", + " 147: 4800,\n", + " 180: 4800,\n", + " 144: 4608,\n", + " 198: 4416,\n", + " 258: 4416,\n", + " 174: 4224,\n", + " 135: 4032,\n", + " 231: 4032,\n", + " 243: 4032,\n", + " 276: 3840,\n", + " 300: 3840,\n", + " 150: 3648,\n", + " 225: 3648,\n", + " 279: 3648,\n", + " 129: 3456,\n", + " 291: 3456,\n", + " 207: 3264,\n", + " 255: 3264,\n", + " 261: 3264,\n", + " 228: 3072,\n", + " 303: 3072,\n", + " 162: 2880,\n", + " 183: 2880,\n", + " 201: 2880,\n", + " 282: 2880,\n", + " 168: 2688,\n", + " 171: 2688,\n", + " 186: 2496,\n", + " 204: 2496,\n", + " 246: 2496,\n", + " 270: 2496,\n", + " 153: 2304,\n", + " 132: 2112,\n", + " 159: 2112,\n", + " 189: 1920,\n", + " 267: 1920,\n", + " 273: 1920,\n", + " 288: 1920,\n", + " 165: 1728,\n", + " 234: 1536,\n", + " 240: 1536,\n", + " 264: 1536,\n", + " 294: 1536,\n", + " 297: 1536,\n", + " 156: 1152,\n", + " 192: 1152,\n", + " 285: 960}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "os_abcd.count_property(\"n_atoms\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import Counter" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 328 ms, sys: 57.3 ms, total: 385 ms\n", + "Wall time: 1.01 s\n" + ] + }, + { + "data": { + "text/plain": [ + "Counter({114: 60672,\n", + " 210: 64128,\n", + " 306: 67200,\n", + " 141: 6144,\n", + " 180: 4800,\n", + " 144: 4608,\n", + " 138: 5184,\n", + " 171: 2688,\n", + " 207: 3264,\n", + " 195: 4992,\n", + " 150: 3648,\n", + " 129: 3456,\n", + " 204: 2496,\n", + " 177: 6912,\n", + " 168: 2688,\n", + " 132: 2112,\n", + " 192: 1152,\n", + " 126: 10752,\n", + " 147: 4800,\n", + " 189: 1920,\n", + " 135: 4032,\n", + " 174: 4224,\n", + " 165: 1728,\n", + " 186: 2496,\n", + " 201: 2880,\n", + " 153: 2304,\n", + " 198: 4416,\n", + " 183: 2880,\n", + " 162: 2880,\n", + " 156: 1152,\n", + " 159: 2112,\n", + " 252: 7104,\n", + " 279: 3648,\n", + " 222: 15168,\n", + " 273: 1920,\n", + " 300: 3840,\n", + " 240: 1536,\n", + " 303: 3072,\n", + " 291: 3456,\n", + " 288: 1920,\n", + " 246: 2496,\n", + " 249: 5184,\n", + " 243: 4032,\n", + " 231: 4032,\n", + " 234: 1536,\n", + " 237: 6528,\n", + " 270: 2496,\n", + " 264: 1536,\n", + " 267: 1920,\n", + " 255: 3264,\n", + " 258: 4416,\n", + " 282: 2880,\n", + " 276: 3840,\n", + " 297: 1536,\n", + " 261: 3264,\n", + " 225: 3648,\n", + " 228: 3072,\n", + " 285: 960,\n", + " 294: 1536})" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "Counter(mongo_abcd.property(\"n_atoms\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 37.5 ms, sys: 86 µs, total: 37.5 ms\n", + "Wall time: 52.8 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "(array([374., 82., 137., 105., 308., 185., 120., 80., 147., 363.]),\n", + " array([-61192.4609375 , -58157.79023438, -55123.11953125, -52088.44882813,\n", + " -49053.778125 , -46019.10742188, -42984.43671875, -39949.76601563,\n", + " -36915.0953125 , -33880.42460937, -30845.75390625]),\n", + " )" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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zBmnnTn1leuNk3SLzSmDRr311luma9euZuXKlaakpMQUFRWZN99802mvra01AwYMMKmpqebgwYNm8+bNpnv37iYnJ8ep+dOf/mQiIiJMdna2KS4uNsuXLzdhYWFmy5YtTs26detMeHi4ee2110xRUZGZMWOGiYqKMmVlZU7NrFmzTEJCgtm5c6c5cOCAGTFihLnvvvu+mYG4jqSkJLN48WJz9uxZZzl//rzTfvLkSSPJ7NixI6CmpqbGqfnwww9NWFiYWbp0qSkuLjYLFiww7du3N0eOHHFqlixZYiIjI80777xj/vjHP5oHHnjA9OrVy3z55ZdOzbhx48zgwYNNQUGB+f3vf2/69OljJk2a9M0MRBNcb6yqqqpMbGysmTx5sjl69Kh54403TMeOHc2vfvUrp6atjJUxxvzkJz8x6enpRpI5ePCgs505FaixcWI+/X8///nPTX5+vjl16pT58MMPjdfrNV6v12lnTn3leuN0q80pQk4jLl26ZG6//XbzX//1X43WbN682YSGhhqfz+dsW7lypXG73aa6utoYY8xTTz1l7r777oDXPfzwwyYtLc1Zv/fee01WVpazXldXZ+Lj401ubq4xxpjKykrTvn17s379eqfm2LFjRpLJz8+/uQNtBklJSWbZsmWNtl/+43HlH9+v+8EPfmAyMjICtqWkpJgf//jHxhhj6uvrjcfjMS+++KLTXllZaVwul3njjTeMMcYUFxcbSebjjz92at577z0TEhJi/vKXv9zAkTW/643VK6+8Yrp27erMH2OMmT9/vunbt6+z3lbGavPmzaZfv36mqKio0ZDDnLr2ODGfGrdhwwYTEhLihBjmVMO+Pk632pzi46pG/OEPf9Bf/vIXhYaGaujQoYqLi1N6erqOHj3q1OTn52vgwIEBX2CYlpYmv9+voqIipyY1NTVg32lpacrPz5ck1dTUqLCwMKAmNDRUqampTk1hYaEuXboUUNOvXz8lJiY6Na1tyZIl6tatm4YOHaoXX3zxqo/spK9Og8bExGjkyJH6n//5n4C2643TyZMn5fP5AmoiIyOVkpLi1OTn5ysqKkrDhw93alJTUxUaGqr9+/c327HerGuNVX5+vkaNGqXw8HBnW1pamkpKSvR///d/To3tY1VWVqYZM2bov//7vxUREdFoXVufU9cbJ+ZTwyoqKrR27Vrdd999at++fUBbW59TV2ponG61OUXIacSf/vQnSdKiRYu0YMECbdy4UV27dtXo0aNVUVEhSfL5fFd9Q/PldZ/Pd80av9+vL7/8Un/9619VV1fXYM2V+wgPD7/qoaRX1rSmn/zkJ1q3bp3ef/99/fjHP9YLL7ygp556ymnv3Lmzfvazn2n9+vXatGmTRo4cqYkTJwb8AWlsnK4cg8vbrlUTExMT0N6uXTtFR0d/K8ZJuv5Y3cycsmWsjDF69NFHNWvWrIA/cFdiTjVtnJhPgebPn69OnTqpW7duKi0t1YYNG5w25tT/d61xutXmVJsLOU8//XSDF5dduXzyySeqr6+XJP3bv/2bMjMzNWzYMK1evVohISFav359Kx9Fy2vqOElfPYds9OjRGjRokGbNmqWf/exnWr58uaqrqyV99dT57OxspaSk6J577tGSJUv0ox/9SC+++GJrHmKzac6xsllTx2n58uU6d+6ccnJyGt2XzXOqOcfJdsH87knSvHnzdPDgQW3btk1hYWGaOnWqzN++9J851bRxutW02rOrWsuTTz6pRx999Jo1vXv31tmzZyVJycnJznaXy6XevXurtLRUkuTxeK66C+ryHU8ej8f536/fBVVWVia3262OHTsqLCxMYWFhDdZcuY+amhpVVlYGnM25sqa5NXWcGpKSkqLa2lqdOnVKffv2bbRm+/btznpj43TlGFzeFhcXF1AzZMgQp6a8vDxgH7W1taqoqGixcZKad6waGwfp+nPq2z5WTR2nXbt2KT8//6pn4AwfPlyTJ0/W66+/3uBrbZlTzTlONs8nKfjfve7du6t79+6666671L9/fyUkJKigoEBer7fB17a1OXXZtcbplptTTb56p42pqqoyLpcr4MLjmpoaExMT41xFfvnC4yvvgvrVr35l3G63uXjxojHmqwuPBwwYELDvSZMmXXXh8Zw5c5z1uro6c/vtt1914fFbb73l1HzyySffmguPv+43v/mNCQ0NNRUVFY3W/OM//qMZOnSos/6DH/zA/P3f/31AjdfrvepCtZdeeslpv/z/0dcvVDtw4IBTs3Xr1m/tBX3GXD1Wly/qu/KOjpycnKsu6rN5rP785z+bI0eOOMvWrVuNJPPWW2+Z06dPN/q6tjanmjJOzKfG/fnPfzaSzPvvv99oTVubUw35+jjdanOKkHMN//zP/2xuv/12s3XrVvPJJ5+Y6dOnm5iYGOcfpMu3kI8dO9YcOnTIbNmyxdx2220N3kI+b948c+zYMbNixYoGbyF3uVxmzZo1pri42MycOdNERUUF3LU1a9Ysk5iYaHbt2mUOHDhw1W19rWXfvn1m2bJl5tChQ+bEiRPmN7/5jbntttvM1KlTnZo1a9aYvLw8c+zYMXPs2DHz/PPPm9DQUPPaa685NR9++KFp166deemll8yxY8fMM8880+Ath1FRUWbDhg3m8OHD5sEHH2zwlsOhQ4ea/fv3mw8++MDceeed35pbM5syVpWVlSY2NtZMmTLFHD161Kxbt85ERERcdXum7WN1pYbuemFOXa2hcWI+faWgoMAsX77cHDx40Jw6dcrs3LnT3HfffeaOO+5w/oOUOdW0cbrV5hQh5xpqamrMk08+aWJiYkyXLl1MamqqOXr0aEDNqVOnTHp6uunYsaPp3r27efLJJ82lS5cCat5//30zZMgQEx4ebnr37m1Wr1591XstX77cJCYmmvDwcHPvvfeagoKCgPYvv/zS/NM//ZPp2rWriYiIMP/wD/9gzp492+zHHKzCwkKTkpJiIiMjTYcOHUz//v3NCy+84PxCGPPVH4/+/fubiIgI43a7zb333htwO/xlv/3tb81dd91lwsPDzd133202bdoU0F5fX29++tOfmtjYWONyucyYMWNMSUlJQM3nn39uJk2aZDp37mzcbrd57LHHzLlz51rm4IPUlLEyxpg//vGPZuTIkcblcpnbb7/dLFmy5Kp92T5WV2os5DCnAjV2CzTzyZjDhw+b733veyY6Otq4XC7Ts2dPM2vWLPPZZ585Ncyppo2TMbfWnAox5ha9mggAAOAa2tzdVQAAoG0g5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASv8P/0Dz0ZdMtTYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%time\n", + "plt.hist(os_abcd.count_property(\"energy\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1.16 s, sys: 46.4 ms, total: 1.2 s\n", + "Wall time: 2.03 s\n" + ] + }, + { + "data": { + "text/plain": [ + "(array([82560., 15744., 26688., 20160., 67776., 37440., 23232., 15552.,\n", + " 28416., 76992.]),\n", + " array([-61192.46163388, -58157.79081243, -55123.11999097, -52088.44916952,\n", + " -49053.77834806, -46019.10752661, -42984.43670515, -39949.7658837 ,\n", + " -36915.09506224, -33880.42424079, -30845.75341933]),\n", + " )" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%time\n", + "plt.hist(mongo_abcd.property(\"energy\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2.09 ms, sys: 0 ns, total: 2.09 ms\n", + "Wall time: 5.36 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "169536" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "query = 'n_atoms: [200 TO 300]'\n", + "os_abcd.count(query)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2.8 ms, sys: 0 ns, total: 2.8 ms\n", + "Wall time: 396 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "169536" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "mongo_query = {\n", + " 'n_atoms': {'$gte': 200, '$lte': 300},\n", + "}\n", + "mongo_abcd.count(mongo_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 3.08 ms, sys: 0 ns, total: 3.08 ms\n", + "Wall time: 6.34 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "60672" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "os_query = 'formula: C48H28O32Zr6 AND username: ubuntu'\n", + "os_abcd.count(os_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 816 µs, sys: 3.33 ms, total: 4.14 ms\n", + "Wall time: 409 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "60672" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "mongo_query = {\n", + " \"formula\": \"C48H28O32Zr6\",\n", + " \"username\": \"ubuntu\",\n", + "\n", + "}\n", + "mongo_abcd.count(mongo_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 4.41 ms, sys: 69 µs, total: 4.48 ms\n", + "Wall time: 7.81 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "394560" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "os_query = 'pbc: true'\n", + "os_abcd.count(os_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 5.05 ms, sys: 246 µs, total: 5.3 ms\n", + "Wall time: 425 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "394560" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time\n", + "mongo_query = {\n", + " 'pbc': True,\n", + "}\n", + "mongo_abcd.count(mongo_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "152\n", + "114\n", + "CPU times: user 133 ms, sys: 7.48 ms, total: 140 ms\n", + "Wall time: 172 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "os_query = 'modified: [* TO 2023-09-06T12:30:32.0000001]'\n", + "print(os_abcd.count(os_query))\n", + "print(next(os_abcd.get_items(os_query))[\"n_atoms\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "152\n", + "114\n", + "CPU times: user 17.8 ms, sys: 3.97 ms, total: 21.8 ms\n", + "Wall time: 688 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "mongo_query = {\n", + " 'modified': {'$lt': datetime.fromisoformat('2023-09-04T11:19:24.310')}\n", + "}\n", + "print(mongo_abcd.count(mongo_query))\n", + "print(next(mongo_abcd.get_items(mongo_query))[\"n_atoms\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "109\n", + "2350.374085846946\n", + "CPU times: user 141 ms, sys: 22.5 ms, total: 164 ms\n", + "Wall time: 194 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "os_query = 'author: Jan* AND Metal: Ag AND Space_group: Pbca'\n", + "print(os_abcd.count(os_query))\n", + "print(next(os_abcd.get_atoms(os_query)).get_volume())" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "109\n", + "2439.0408078794635\n", + "CPU times: user 32.7 ms, sys: 7.99 ms, total: 40.7 ms\n", + "Wall time: 1.25 s\n" + ] + } + ], + "source": [ + "%%time\n", + "mongo_query = {\n", + " \"author\": {'$regex': 'Jan*'},\n", + " \"Metal\": \"Ag\",\n", + " \"Space_group\": \"Pbca\"\n", + "}\n", + "print(mongo_abcd.count(mongo_query))\n", + "print(next(mongo_abcd.get_atoms(mongo_query)).get_volume())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv_9", + "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.10.13" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/abcd_opensearch_properties.ipynb b/tutorials/abcd_opensearch_properties.ipynb new file mode 100644 index 00000000..1862549d --- /dev/null +++ b/tutorials/abcd_opensearch_properties.ipynb @@ -0,0 +1,640 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "# Usage of ABCD database with extra information" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "pycharm": { + "is_executing": false + } + }, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "pycharm": { + "is_executing": false + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from abcd import ABCD\n", + "from abcd.backends.atoms_properties import Properties" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "First of all, we need to define the url of the database. It could be local or remote:\n", + "\n", + "- direct access: url = 'opensearch://admin:admin@localhost:9200'\n", + "- api access: url = 'http://localhost/api'\n", + "\n", + "using with statement to catch the riased exceptions. You may can ignore them but in that case need to handle all the unexpected events. (cannot connect to db, lost connection, wrong filter, wrong url, etc. )" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "pycharm": { + "is_executing": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenSearchDatabase(url=localhost:9200, index=atoms) \n" + ] + } + ], + "source": [ + "url = 'opensearch://admin:admin@localhost:9200'\n", + "abcd = ABCD.from_url(url)\n", + "\n", + "print(abcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 0\n", + " type: opensearch\n" + ] + } + ], + "source": [ + "abcd.print_info()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "## Cleanup \n", + "\n", + "WARNING!! Remove all elements from the database.\n", + "Only supported in the case of local access" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.destroy()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.create()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 0\n", + " type: opensearch\n" + ] + } + ], + "source": [ + "abcd.print_info()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "## Uploading configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/ubuntu/abcd/tutorials\n" + ] + } + ], + "source": [ + "from ase.io import iread, read\n", + "!pwd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Data can be entered into the database as ASE Atoms objects, allowing any format readable by ase.io.read to be used." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "directory = Path('/home/ubuntu/data/')\n", + "file = directory / 'input.data.2055.xyz'" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 15 s, sys: 181 ms, total: 15.2 s\n", + "Wall time: 25 s\n" + ] + } + ], + "source": [ + "%%time\n", + "with abcd as db:\n", + " for atoms in iread(file.as_posix(), index=slice(None)):\n", + " db.push(atoms, store_calc=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Extra information can be added manually via a dictionary, or read in through a csv/Excel file. A template for the structures corresponding to each row in the data file, and units in the form of `field (unit)` or `field / unit`, can also be inferred." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "pycharm": { + "is_executing": false, + "metadata": false, + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "directory = Path('/home/ubuntu/data/')\n", + "data_file = directory / 'DATA_copy.csv'\n", + "struct_file_template = str(directory) + \"/{struct_name}_FSR-out.cif\"" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "pycharm": { + "is_executing": false + }, + "scrolled": false + }, + "outputs": [], + "source": [ + "properties = Properties(\n", + " data_file=data_file,\n", + " store_struct_file=True,\n", + " struct_file_template=struct_file_template,\n", + " struct_name_label = \"MOF_name\",\n", + " infer_units=True,\n", + " # units={\"Density\": \"g/cm3\"}\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using the inferred structure file, this data can then be uploaded together. The `extra_info`, and properties in general, do not need to match that of existing documents stored." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "for i, data in enumerate(properties.to_list()):\n", + " if data['MOF_name'] == \"EWIKAX03\":\n", + " atoms = read(properties.struct_files[i])\n", + " with abcd as db:\n", + " db.push(\n", + " atoms,\n", + " store_calc=False,\n", + " extra_info=data,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Dos at Fermi energy': 'eln/cell',\n", + " 'Dos at VBM': 'eln/cell',\n", + " 'Dos at CBM': 'eln/cell',\n", + " 'Density': 'g/cm3',\n", + " 'Accessible Surface Area': 'm2/g'}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[\"units\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 2056\n", + " type: opensearch\n" + ] + } + ], + "source": [ + "abcd.print_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'spacegroup': Spacegroup(1, setting=1), 'unit_cell': 'conventional', 'occupancy': {'0': {'H': 1.0}, '1': {'H': 1.0}, '2': {'H': 1.0}, '3': {'H': 1.0}, '4': {'H': 1.0}, '5': {'H': 1.0}, '6': {'H': 1.0}, '7': {'H': 1.0}, '8': {'H': 1.0}, '9': {'H': 1.0}, '10': {'H': 1.0}, '11': {'H': 1.0}, '12': {'H': 1.0}, '13': {'H': 1.0}, '14': {'H': 1.0}, '15': {'H': 1.0}, '16': {'H': 1.0}, '17': {'H': 1.0}, '18': {'H': 1.0}, '19': {'H': 1.0}, '20': {'H': 1.0}, '21': {'H': 1.0}, '22': {'H': 1.0}, '23': {'H': 1.0}, '24': {'H': 1.0}, '25': {'H': 1.0}, '26': {'H': 1.0}, '27': {'H': 1.0}, '28': {'H': 1.0}, '29': {'H': 1.0}, '30': {'H': 1.0}, '31': {'H': 1.0}, '32': {'H': 1.0}, '33': {'H': 1.0}, '34': {'H': 1.0}, '35': {'H': 1.0}, '36': {'H': 1.0}, '37': {'H': 1.0}, '38': {'H': 1.0}, '39': {'H': 1.0}, '40': {'H': 1.0}, '41': {'H': 1.0}, '42': {'H': 1.0}, '43': {'H': 1.0}, '44': {'H': 1.0}, '45': {'H': 1.0}, '46': {'H': 1.0}, '47': {'H': 1.0}, '48': {'H': 1.0}, '49': {'H': 1.0}, '50': {'H': 1.0}, '51': {'H': 1.0}, '52': {'H': 1.0}, '53': {'H': 1.0}, '54': {'H': 1.0}, '55': {'H': 1.0}, '56': {'H': 1.0}, '57': {'H': 1.0}, '58': {'H': 1.0}, '59': {'H': 1.0}, '60': {'H': 1.0}, '61': {'H': 1.0}, '62': {'H': 1.0}, '63': {'H': 1.0}, '64': {'C': 1.0}, '65': {'C': 1.0}, '66': {'C': 1.0}, '67': {'C': 1.0}, '68': {'C': 1.0}, '69': {'C': 1.0}, '70': {'C': 1.0}, '71': {'C': 1.0}, '72': {'C': 1.0}, '73': {'C': 1.0}, '74': {'C': 1.0}, '75': {'C': 1.0}, '76': {'C': 1.0}, '77': {'C': 1.0}, '78': {'C': 1.0}, '79': {'C': 1.0}, '80': {'C': 1.0}, '81': {'C': 1.0}, '82': {'C': 1.0}, '83': {'C': 1.0}, '84': {'C': 1.0}, '85': {'C': 1.0}, '86': {'C': 1.0}, '87': {'C': 1.0}, '88': {'C': 1.0}, '89': {'C': 1.0}, '90': {'C': 1.0}, '91': {'C': 1.0}, '92': {'C': 1.0}, '93': {'C': 1.0}, '94': {'C': 1.0}, '95': {'C': 1.0}, '96': {'C': 1.0}, '97': {'C': 1.0}, '98': {'C': 1.0}, '99': {'C': 1.0}, '100': {'C': 1.0}, '101': {'C': 1.0}, '102': {'C': 1.0}, '103': {'C': 1.0}, '104': {'C': 1.0}, '105': {'C': 1.0}, '106': {'C': 1.0}, '107': {'C': 1.0}, '108': {'C': 1.0}, '109': {'C': 1.0}, '110': {'C': 1.0}, '111': {'C': 1.0}, '112': {'N': 1.0}, '113': {'N': 1.0}, '114': {'N': 1.0}, '115': {'N': 1.0}, '116': {'N': 1.0}, '117': {'N': 1.0}, '118': {'N': 1.0}, '119': {'N': 1.0}, '120': {'N': 1.0}, '121': {'N': 1.0}, '122': {'N': 1.0}, '123': {'N': 1.0}, '124': {'N': 1.0}, '125': {'N': 1.0}, '126': {'N': 1.0}, '127': {'N': 1.0}, '128': {'N': 1.0}, '129': {'N': 1.0}, '130': {'N': 1.0}, '131': {'N': 1.0}, '132': {'N': 1.0}, '133': {'N': 1.0}, '134': {'N': 1.0}, '135': {'N': 1.0}, '136': {'Fe': 1.0}, '137': {'Fe': 1.0}, '138': {'Fe': 1.0}, '139': {'Fe': 1.0}}}\n" + ] + } + ], + "source": [ + "print(atoms.info)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "pycharm": {} + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'info': ['1aromatico-up', '2D', '2aromatici-up', '5-m-rings', '5m-ring-leg2met', '6m-rings', 'Accessible Surface Area', 'Band_gap', 'CN-M', 'COOM', 'Cell volume', 'Crit: metal', 'Crit: pi-pi stacking', 'Crit: redox active linker', 'Crit: redox match', 'Criteria#', 'Density', 'Dos at CBM', 'Dos at Fermi energy', 'Dos at VBM', 'HSE band gap', 'LCD', 'M-C-C-TRIANG', 'M-H2O-M', 'M-N-NM-N-M', 'M-h2o', 'MOF_name', 'Metal', 'Metal density', 'Metals number', 'Multiplier_Sum', 'N3--NCN up', 'PLD', 'Space_group', 'Space_group#', 'Temp', 'Volume Fraction', 'Year', 'Zprime', 'benzene', 'cell', 'energy', 'formula', 'metal-N', 'metal-O', 'metal-S', 'metal-halogen', 'n_atoms', 'occupancy', 'pbc', 'pyridine', 'pyrimidine', 'spacegroup', 'unit_cell', 'units', 'volume', 'without ions'], 'derived': ['elements', 'hash', 'hash_structure', 'modified', 'uploaded', 'username', 'volume'], 'arrays': ['forces', 'numbers', 'positions', 'spacegroup_kinds']}\n" + ] + } + ], + "source": [ + "print(abcd.properties())" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{29.868799209594727: 1}\n" + ] + } + ], + "source": [ + "# print(abcd.property(\"6m-rings\"))\n", + "print(abcd.count_property(\"Dos at Fermi energy\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "{'_id': 'J4TzUYoBQvF7oZdWKd8C', 'n_atoms': 140, 'cell': [[11.7598, 0.0, 0.0], [0.0, 11.9363, 0.0], [0.0, 0.0, 13.9234]], 'pbc': [True, True, True], 'formula': 'C48H64Fe4N24', 'numbers': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 26, 26, 26, 26], 'positions': [[1.1533188654, 6.568474272199999, 0.33047189900000007], [0.8348164422, 5.6740515043, 1.8464934612000004], [1.6283559864000001, 7.2832437888, 1.9067817832000002], [6.386829698600001, 4.7772056675, 0.5637027724000001], [5.3133598752, 5.427829507899999, 1.8410076416000005], [6.8133223651999995, 4.549222337500001, 2.290538534], [8.8241070476, 6.8505767864, 3.7879027402000007], [8.8195795246, 6.854754491399999, 6.227811509400001], [4.7265811346, 11.3359757278, 0.33047189900000007], [5.0450835578, 0.2940984957000009, 1.8464934612000004], [4.2515440136, 10.621206211199999, 1.9067817832000002], [11.252870301399998, 1.1909443325, 0.5637027724000001], [0.5665401247999999, 0.5403204921, 1.8410076416000005], [10.8263776348, 1.4189276624999998, 2.290538534], [8.815592952400001, 11.0538732136, 3.7879027402000007], [8.8201204754, 11.0496955086, 6.227811509400001], [1.1533188654, 11.3359757278, 7.2921718989999995], [0.8348164422, 0.2940984957000009, 8.8081934612], [1.6283559864000001, 10.621206211199999, 8.8684817832], [6.386829698600001, 1.1909443325, 7.5254027724000006], [5.3133598752, 0.5403204921, 8.802707641600001], [6.8133223651999995, 1.4189276624999998, 9.252238534000004], [8.8241070476, 11.0538732136, 10.7496027402], [8.8195795246, 11.0496955086, 13.189511509400003], [4.7265811346, 6.568474272199999, 7.2921718989999995], [5.0450835578, 5.6740515043, 8.8081934612], [4.2515440136, 7.2832437888, 8.8684817832], [11.252870301399998, 4.7772056675, 7.5254027724000006], [0.5665401247999999, 5.427829507899999, 8.802707641600001], [10.8263776348, 4.549222337500001, 9.252238534000004], [8.815592952400001, 6.8505767864, 10.7496027402], [8.8201204754, 6.854754491399999, 13.189511509400003], [10.606481134600001, 0.6003242722, 6.631228101000001], [10.924983557800001, 11.642213440599999, 5.1152065388], [10.1314440136, 1.3150937887999998, 5.0549182168], [5.3729703014, 10.745355667500002, 6.397997227600001], [6.4464401248000005, 11.3959795079, 5.120692358400001], [4.9464776348, 10.5173723375, 4.671161466], [2.9356929524, 0.8824267863999998, 3.1737972598], [2.9402204754, 0.8866044913999999, 0.7338884906000002], [7.033218865399999, 5.3678257278, 6.631228101000001], [6.714716442199999, 6.2622365594, 5.1152065388], [7.508255986400001, 4.6530562112, 5.0549182168], [0.5069296986, 7.159094332499999, 6.397997227600001], [11.1932598752, 6.508470492099999, 5.120692358400001], [0.9334223652, 7.3870776624999985, 4.671161466], [2.9442070476, 5.0857232136, 3.1737972598], [2.9396795246000003, 5.0815455086, 0.7338884906000002], [10.606481134600001, 5.3678257278, 13.592928101000004], [10.924983557800001, 6.2622365594, 12.076906538800003], [10.1314440136, 4.6530562112, 12.016618216800003], [5.3729703014, 7.159094332499999, 13.3596972276], [6.4464401248000005, 6.508470492099999, 12.0823923584], [4.9464776348, 7.3870776624999985, 11.632861466], [2.9356929524, 5.0857232136, 10.135497259800003], [2.9402204754, 5.0815455086, 7.6955884906000005], [7.033218865399999, 0.6003242722, 13.592928101000004], [6.714716442199999, 11.642213440599999, 12.076906538800003], [7.508255986400001, 1.3150937887999998, 12.016618216800003], [0.5069296986, 10.745355667500002, 13.3596972276], [11.1932598752, 11.3959795079, 12.0823923584], [0.9334223652, 10.5173723375, 11.632861466], [2.9442070476, 0.8824267863999998, 10.135497259800003], [2.9396795246000003, 0.8866044913999999, 7.6955884906000005], [11.3421624628, 7.352343029499999, 1.4700882656000003], [0.8413313713999999, 6.690045487699999, 1.3900565624000003], [7.1397744132, 6.4757292212, 1.541181146], [6.3813143524, 5.266367177799999, 1.5607156762000003], [8.816380858999999, 7.8135974704, 4.3231043128], [8.8125824436, 7.814814973, 5.689045546400001], [6.297537537200001, 10.552106970499999, 1.4700882656000003], [5.0385686286, 11.2144045123, 1.3900565624000003], [10.499925586800002, 11.428720778799999, 1.541181146], [11.2583856476, 0.7017828222, 1.5607156762000003], [8.823319141, 10.0908525296, 4.3231043128], [8.827117556400001, 10.089635027, 5.689045546400001], [11.3421624628, 10.552106970499999, 8.4317882656], [0.8413313713999999, 11.2144045123, 8.3517565624], [7.1397744132, 11.428720778799999, 8.502881146], [6.3813143524, 0.7017828222, 8.522415676200001], [8.816380858999999, 10.0908525296, 11.2848043128], [8.8125824436, 10.089635027, 12.6507455464], [6.297537537200001, 7.352343029499999, 8.4317882656], [5.0385686286, 6.690045487699999, 8.3517565624], [10.499925586800002, 6.4757292212, 8.502881146], [11.2583856476, 5.266367177799999, 8.522415676200001], [8.823319141, 7.8135974704, 11.2848043128], [8.827117556400001, 7.814814973, 12.6507455464], [0.41763753719999996, 1.3841930295, 5.491611734400001], [10.9184686286, 0.7218954876999999, 5.5716434376000015], [4.6200255868, 0.5075792212000001, 5.420518854], [5.378485647599999, 11.234517177799999, 5.400984323800001], [2.943419141, 1.8454474704, 2.6385956872000005], [2.9472175564, 1.8466649729999998, 1.2726544536000002], [5.4622624628, 4.583956970500001, 5.491611734400001], [6.7212313714, 5.246254512299999, 5.5716434376000015], [1.2598744132, 5.460570778799999, 5.420518854], [0.5014143524, 6.6699328222, 5.400984323800001], [2.936480859, 4.1227025296, 2.6385956872000005], [2.9326824436, 4.121485026999999, 1.2726544536000002], [0.41763753719999996, 4.583956970500001, 12.453311734400001], [10.9184686286, 5.246254512299999, 12.533343437600001], [4.6200255868, 5.460570778799999, 12.382218854000003], [5.378485647599999, 6.6699328222, 12.3626843238], [2.943419141, 4.1227025296, 9.600295687200003], [2.9472175564, 4.121485026999999, 8.234354453600004], [5.4622624628, 1.3841930295, 12.453311734400001], [6.7212313714, 0.7218954876999999, 12.533343437600001], [1.2598744132, 0.5075792212000001, 12.382218854000003], [0.5014143524, 11.234517177799999, 12.3626843238], [2.936480859, 1.8454474704, 9.600295687200003], [2.9326824436, 1.8466649729999998, 8.234354453600004], [10.3377932242, 7.937329156199999, 1.5133204226], [7.7886801772, 7.4419965787999995, 1.5317967744], [7.301906775799999, 9.9671208438, 1.5133204226], [9.851019822800001, 10.462453421200001, 1.5317967744], [10.3377932242, 9.9671208438, 8.475020422600002], [7.7886801772, 10.462453421200001, 8.4934967744], [7.301906775799999, 7.937329156199999, 8.475020422600002], [9.851019822800001, 7.4419965787999995, 8.4934967744], [8.81985, 8.952224999999999, 3.551246710400001], [8.81985, 8.952224999999999, 6.462337259000002], [8.81985, 8.952224999999999, 10.5129467104], [8.81985, 8.952224999999999, 13.424037259000002], [1.4220067758000001, 1.9691791562, 5.448379577400002], [3.9711198228, 1.4738465788, 5.429903225600001], [4.4578932242, 3.9989708437999996, 5.448379577400002], [1.9087801772000001, 4.4943034212, 5.429903225600001], [1.4220067758000001, 3.9989708437999996, 12.4100795774], [3.9711198228, 4.4943034212, 12.391603225600003], [4.4578932242, 1.9691791562, 12.4100795774], [1.9087801772000001, 1.4738465788, 12.391603225600003], [2.93995, 2.984075, 3.4104532896], [2.93995, 2.984075, 0.49936274100000005], [2.93995, 2.984075, 10.372153289600003], [2.93995, 2.984075, 7.461062741000001], [8.81985, 8.952224999999999, 1.5279956862], [8.81985, 8.952224999999999, 8.489695686200001], [2.93995, 2.984075, 5.433704313800002], [2.93995, 2.984075, 12.395404313800002]], 'spacegroup_kinds': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139], 'spacegroup': {'number': 1, 'setting': 1}, 'unit_cell': 'conventional', 'occupancy': {'0': {'H': 1.0}, '1': {'H': 1.0}, '2': {'H': 1.0}, '3': {'H': 1.0}, '4': {'H': 1.0}, '5': {'H': 1.0}, '6': {'H': 1.0}, '7': {'H': 1.0}, '8': {'H': 1.0}, '9': {'H': 1.0}, '10': {'H': 1.0}, '11': {'H': 1.0}, '12': {'H': 1.0}, '13': {'H': 1.0}, '14': {'H': 1.0}, '15': {'H': 1.0}, '16': {'H': 1.0}, '17': {'H': 1.0}, '18': {'H': 1.0}, '19': {'H': 1.0}, '20': {'H': 1.0}, '21': {'H': 1.0}, '22': {'H': 1.0}, '23': {'H': 1.0}, '24': {'H': 1.0}, '25': {'H': 1.0}, '26': {'H': 1.0}, '27': {'H': 1.0}, '28': {'H': 1.0}, '29': {'H': 1.0}, '30': {'H': 1.0}, '31': {'H': 1.0}, '32': {'H': 1.0}, '33': {'H': 1.0}, '34': {'H': 1.0}, '35': {'H': 1.0}, '36': {'H': 1.0}, '37': {'H': 1.0}, '38': {'H': 1.0}, '39': {'H': 1.0}, '40': {'H': 1.0}, '41': {'H': 1.0}, '42': {'H': 1.0}, '43': {'H': 1.0}, '44': {'H': 1.0}, '45': {'H': 1.0}, '46': {'H': 1.0}, '47': {'H': 1.0}, '48': {'H': 1.0}, '49': {'H': 1.0}, '50': {'H': 1.0}, '51': {'H': 1.0}, '52': {'H': 1.0}, '53': {'H': 1.0}, '54': {'H': 1.0}, '55': {'H': 1.0}, '56': {'H': 1.0}, '57': {'H': 1.0}, '58': {'H': 1.0}, '59': {'H': 1.0}, '60': {'H': 1.0}, '61': {'H': 1.0}, '62': {'H': 1.0}, '63': {'H': 1.0}, '64': {'C': 1.0}, '65': {'C': 1.0}, '66': {'C': 1.0}, '67': {'C': 1.0}, '68': {'C': 1.0}, '69': {'C': 1.0}, '70': {'C': 1.0}, '71': {'C': 1.0}, '72': {'C': 1.0}, '73': {'C': 1.0}, '74': {'C': 1.0}, '75': {'C': 1.0}, '76': {'C': 1.0}, '77': {'C': 1.0}, '78': {'C': 1.0}, '79': {'C': 1.0}, '80': {'C': 1.0}, '81': {'C': 1.0}, '82': {'C': 1.0}, '83': {'C': 1.0}, '84': {'C': 1.0}, '85': {'C': 1.0}, '86': {'C': 1.0}, '87': {'C': 1.0}, '88': {'C': 1.0}, '89': {'C': 1.0}, '90': {'C': 1.0}, '91': {'C': 1.0}, '92': {'C': 1.0}, '93': {'C': 1.0}, '94': {'C': 1.0}, '95': {'C': 1.0}, '96': {'C': 1.0}, '97': {'C': 1.0}, '98': {'C': 1.0}, '99': {'C': 1.0}, '100': {'C': 1.0}, '101': {'C': 1.0}, '102': {'C': 1.0}, '103': {'C': 1.0}, '104': {'C': 1.0}, '105': {'C': 1.0}, '106': {'C': 1.0}, '107': {'C': 1.0}, '108': {'C': 1.0}, '109': {'C': 1.0}, '110': {'C': 1.0}, '111': {'C': 1.0}, '112': {'N': 1.0}, '113': {'N': 1.0}, '114': {'N': 1.0}, '115': {'N': 1.0}, '116': {'N': 1.0}, '117': {'N': 1.0}, '118': {'N': 1.0}, '119': {'N': 1.0}, '120': {'N': 1.0}, '121': {'N': 1.0}, '122': {'N': 1.0}, '123': {'N': 1.0}, '124': {'N': 1.0}, '125': {'N': 1.0}, '126': {'N': 1.0}, '127': {'N': 1.0}, '128': {'N': 1.0}, '129': {'N': 1.0}, '130': {'N': 1.0}, '131': {'N': 1.0}, '132': {'N': 1.0}, '133': {'N': 1.0}, '134': {'N': 1.0}, '135': {'N': 1.0}, '136': {'Fe': 1.0}, '137': {'Fe': 1.0}, '138': {'Fe': 1.0}, '139': {'Fe': 1.0}}, 'MOF_name': 'EWIKAX03', 'Dos at Fermi energy': 29.8688, 'Band_gap': 0.004466204, 'Dos at VBM': 29.8688, 'Dos at CBM': 29.8688, 'HSE band gap': 'not found', 'LCD': '4.36078', 'PLD': '1.82778', 'Density': '1.696', 'Accessible Surface Area': '0', 'Volume Fraction': '0.51294', 'Criteria#': 2, 'Multiplier_Sum': '1', 'Space_group#': '14', 'Space_group': 'P21/c', 'Temp': '94', 'Zprime': '1', 'Year': '2008', 'Metal': 'Fe', 'Metals number': '1', 'Cell volume': '1954.407', 'Metal density': '0.000511664', 'Crit: metal': 'yes', 'Crit: redox match': 'yes', 'Crit: redox active linker': 'no', 'Crit: pi-pi stacking': 'no', 'without ions': 'no', '2D': 'no', '1aromatico-up': 'yes', '2aromatici-up': 'no', 'N3--NCN up': 'no', 'benzene': 'no', '6m-rings': 'yes', 'pyridine': 'no', 'pyrimidine': 'no', 'metal-S': 'no', 'metal-O': 'no', 'metal-N': 'yes', 'metal-halogen': 'no', '5m-ring-leg2met': 'no', '5-m-rings': 'no', 'CN-M': 'yes', 'M-h2o': 'no', 'M-H2O-M': 'no', 'M-N-NM-N-M': 'no', 'M-C-C-TRIANG': 'no', 'COOM': 'no', 'units': {'Dos at Fermi energy': 'eln/cell', 'Dos at VBM': 'eln/cell', 'Dos at CBM': 'eln/cell', 'Density': 'g/cm3', 'Accessible Surface Area': 'm2/g'}, 'volume': 1954.406783203316, 'elements': {'1': 64, '6': 48, '7': 24, '26': 4}, 'username': 'ubuntu', 'uploaded': '2023-09-01T18:13:24.859685', 'modified': '2023-09-01T18:13:24.859694', 'hash_structure': '660d3f56483cf3a6dd94d833d4478fcf', 'hash': '2c1caa8af0562303fc8cfe0eba64b444', 'derived': {'arrays_keys': ['numbers', 'spacegroup_kinds', 'positions'], 'info_keys': ['pbc', 'formula', 'occupancy', 'cell', 'unit_cell', 'spacegroup', 'n_atoms', 'MOF_name', 'Dos at Fermi energy', 'Band_gap', 'Dos at VBM', 'Dos at CBM', 'HSE band gap', 'LCD', 'PLD', 'Density', 'Accessible Surface Area', 'Volume Fraction', 'Criteria#', 'Multiplier_Sum', 'Space_group#', 'Space_group', 'Temp', 'Zprime', 'Year', 'Metal', 'Metals number', 'Cell volume', 'Metal density', 'Crit: metal', 'Crit: redox match', 'Crit: redox active linker', 'Crit: pi-pi stacking', 'without ions', '2D', '1aromatico-up', '2aromatici-up', 'N3--NCN up', 'benzene', '6m-rings', 'pyridine', 'pyrimidine', 'metal-S', 'metal-O', 'metal-N', 'metal-halogen', '5m-ring-leg2met', '5-m-rings', 'CN-M', 'M-h2o', 'M-H2O-M', 'M-N-NM-N-M', 'M-C-C-TRIANG', 'COOM', 'units', 'volume'], 'results_keys': [], 'derived_keys': ['elements', 'username', 'uploaded', 'modified', 'volume', 'hash_structure', 'hash']}}\n" + ] + } + ], + "source": [ + "query = 'n_atoms: 140'\n", + "print(len(list(abcd.get_items(query))))\n", + "print(list(abcd.get_items(query))[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "query = 'Accessible Surface*'\n", + "print(len(list(abcd.get_items(query))))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "query = 'Year: [2006 TO 2009]'\n", + "print(len(list(abcd.get_items(query))))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2056\n" + ] + } + ], + "source": [ + "query = '*ubuntu'\n", + "print(len(list(abcd.get_items(query))))" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "316\n" + ] + } + ], + "source": [ + "query = 'username:[ubunta TO ubuntx] AND formula:?48H28O32Zr6'\n", + "print(len(list(abcd.get_items(query))))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "{'_id': 'J4TzUYoBQvF7oZdWKd8C', 'n_atoms': 140, 'cell': [[11.7598, 0.0, 0.0], [0.0, 11.9363, 0.0], [0.0, 0.0, 13.9234]], 'pbc': [True, True, True], 'formula': 'C48H64Fe4N24', 'numbers': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 26, 26, 26, 26], 'positions': [[1.1533188654, 6.568474272199999, 0.33047189900000007], [0.8348164422, 5.6740515043, 1.8464934612000004], [1.6283559864000001, 7.2832437888, 1.9067817832000002], [6.386829698600001, 4.7772056675, 0.5637027724000001], [5.3133598752, 5.427829507899999, 1.8410076416000005], [6.8133223651999995, 4.549222337500001, 2.290538534], [8.8241070476, 6.8505767864, 3.7879027402000007], [8.8195795246, 6.854754491399999, 6.227811509400001], [4.7265811346, 11.3359757278, 0.33047189900000007], [5.0450835578, 0.2940984957000009, 1.8464934612000004], [4.2515440136, 10.621206211199999, 1.9067817832000002], [11.252870301399998, 1.1909443325, 0.5637027724000001], [0.5665401247999999, 0.5403204921, 1.8410076416000005], [10.8263776348, 1.4189276624999998, 2.290538534], [8.815592952400001, 11.0538732136, 3.7879027402000007], [8.8201204754, 11.0496955086, 6.227811509400001], [1.1533188654, 11.3359757278, 7.2921718989999995], [0.8348164422, 0.2940984957000009, 8.8081934612], [1.6283559864000001, 10.621206211199999, 8.8684817832], [6.386829698600001, 1.1909443325, 7.5254027724000006], [5.3133598752, 0.5403204921, 8.802707641600001], [6.8133223651999995, 1.4189276624999998, 9.252238534000004], [8.8241070476, 11.0538732136, 10.7496027402], [8.8195795246, 11.0496955086, 13.189511509400003], [4.7265811346, 6.568474272199999, 7.2921718989999995], [5.0450835578, 5.6740515043, 8.8081934612], [4.2515440136, 7.2832437888, 8.8684817832], [11.252870301399998, 4.7772056675, 7.5254027724000006], [0.5665401247999999, 5.427829507899999, 8.802707641600001], [10.8263776348, 4.549222337500001, 9.252238534000004], [8.815592952400001, 6.8505767864, 10.7496027402], [8.8201204754, 6.854754491399999, 13.189511509400003], [10.606481134600001, 0.6003242722, 6.631228101000001], [10.924983557800001, 11.642213440599999, 5.1152065388], [10.1314440136, 1.3150937887999998, 5.0549182168], [5.3729703014, 10.745355667500002, 6.397997227600001], [6.4464401248000005, 11.3959795079, 5.120692358400001], [4.9464776348, 10.5173723375, 4.671161466], [2.9356929524, 0.8824267863999998, 3.1737972598], [2.9402204754, 0.8866044913999999, 0.7338884906000002], [7.033218865399999, 5.3678257278, 6.631228101000001], [6.714716442199999, 6.2622365594, 5.1152065388], [7.508255986400001, 4.6530562112, 5.0549182168], [0.5069296986, 7.159094332499999, 6.397997227600001], [11.1932598752, 6.508470492099999, 5.120692358400001], [0.9334223652, 7.3870776624999985, 4.671161466], [2.9442070476, 5.0857232136, 3.1737972598], [2.9396795246000003, 5.0815455086, 0.7338884906000002], [10.606481134600001, 5.3678257278, 13.592928101000004], [10.924983557800001, 6.2622365594, 12.076906538800003], [10.1314440136, 4.6530562112, 12.016618216800003], [5.3729703014, 7.159094332499999, 13.3596972276], [6.4464401248000005, 6.508470492099999, 12.0823923584], [4.9464776348, 7.3870776624999985, 11.632861466], [2.9356929524, 5.0857232136, 10.135497259800003], [2.9402204754, 5.0815455086, 7.6955884906000005], [7.033218865399999, 0.6003242722, 13.592928101000004], [6.714716442199999, 11.642213440599999, 12.076906538800003], [7.508255986400001, 1.3150937887999998, 12.016618216800003], [0.5069296986, 10.745355667500002, 13.3596972276], [11.1932598752, 11.3959795079, 12.0823923584], [0.9334223652, 10.5173723375, 11.632861466], [2.9442070476, 0.8824267863999998, 10.135497259800003], [2.9396795246000003, 0.8866044913999999, 7.6955884906000005], [11.3421624628, 7.352343029499999, 1.4700882656000003], [0.8413313713999999, 6.690045487699999, 1.3900565624000003], [7.1397744132, 6.4757292212, 1.541181146], [6.3813143524, 5.266367177799999, 1.5607156762000003], [8.816380858999999, 7.8135974704, 4.3231043128], [8.8125824436, 7.814814973, 5.689045546400001], [6.297537537200001, 10.552106970499999, 1.4700882656000003], [5.0385686286, 11.2144045123, 1.3900565624000003], [10.499925586800002, 11.428720778799999, 1.541181146], [11.2583856476, 0.7017828222, 1.5607156762000003], [8.823319141, 10.0908525296, 4.3231043128], [8.827117556400001, 10.089635027, 5.689045546400001], [11.3421624628, 10.552106970499999, 8.4317882656], [0.8413313713999999, 11.2144045123, 8.3517565624], [7.1397744132, 11.428720778799999, 8.502881146], [6.3813143524, 0.7017828222, 8.522415676200001], [8.816380858999999, 10.0908525296, 11.2848043128], [8.8125824436, 10.089635027, 12.6507455464], [6.297537537200001, 7.352343029499999, 8.4317882656], [5.0385686286, 6.690045487699999, 8.3517565624], [10.499925586800002, 6.4757292212, 8.502881146], [11.2583856476, 5.266367177799999, 8.522415676200001], [8.823319141, 7.8135974704, 11.2848043128], [8.827117556400001, 7.814814973, 12.6507455464], [0.41763753719999996, 1.3841930295, 5.491611734400001], [10.9184686286, 0.7218954876999999, 5.5716434376000015], [4.6200255868, 0.5075792212000001, 5.420518854], [5.378485647599999, 11.234517177799999, 5.400984323800001], [2.943419141, 1.8454474704, 2.6385956872000005], [2.9472175564, 1.8466649729999998, 1.2726544536000002], [5.4622624628, 4.583956970500001, 5.491611734400001], [6.7212313714, 5.246254512299999, 5.5716434376000015], [1.2598744132, 5.460570778799999, 5.420518854], [0.5014143524, 6.6699328222, 5.400984323800001], [2.936480859, 4.1227025296, 2.6385956872000005], [2.9326824436, 4.121485026999999, 1.2726544536000002], [0.41763753719999996, 4.583956970500001, 12.453311734400001], [10.9184686286, 5.246254512299999, 12.533343437600001], [4.6200255868, 5.460570778799999, 12.382218854000003], [5.378485647599999, 6.6699328222, 12.3626843238], [2.943419141, 4.1227025296, 9.600295687200003], [2.9472175564, 4.121485026999999, 8.234354453600004], [5.4622624628, 1.3841930295, 12.453311734400001], [6.7212313714, 0.7218954876999999, 12.533343437600001], [1.2598744132, 0.5075792212000001, 12.382218854000003], [0.5014143524, 11.234517177799999, 12.3626843238], [2.936480859, 1.8454474704, 9.600295687200003], [2.9326824436, 1.8466649729999998, 8.234354453600004], [10.3377932242, 7.937329156199999, 1.5133204226], [7.7886801772, 7.4419965787999995, 1.5317967744], [7.301906775799999, 9.9671208438, 1.5133204226], [9.851019822800001, 10.462453421200001, 1.5317967744], [10.3377932242, 9.9671208438, 8.475020422600002], [7.7886801772, 10.462453421200001, 8.4934967744], [7.301906775799999, 7.937329156199999, 8.475020422600002], [9.851019822800001, 7.4419965787999995, 8.4934967744], [8.81985, 8.952224999999999, 3.551246710400001], [8.81985, 8.952224999999999, 6.462337259000002], [8.81985, 8.952224999999999, 10.5129467104], [8.81985, 8.952224999999999, 13.424037259000002], [1.4220067758000001, 1.9691791562, 5.448379577400002], [3.9711198228, 1.4738465788, 5.429903225600001], [4.4578932242, 3.9989708437999996, 5.448379577400002], [1.9087801772000001, 4.4943034212, 5.429903225600001], [1.4220067758000001, 3.9989708437999996, 12.4100795774], [3.9711198228, 4.4943034212, 12.391603225600003], [4.4578932242, 1.9691791562, 12.4100795774], [1.9087801772000001, 1.4738465788, 12.391603225600003], [2.93995, 2.984075, 3.4104532896], [2.93995, 2.984075, 0.49936274100000005], [2.93995, 2.984075, 10.372153289600003], [2.93995, 2.984075, 7.461062741000001], [8.81985, 8.952224999999999, 1.5279956862], [8.81985, 8.952224999999999, 8.489695686200001], [2.93995, 2.984075, 5.433704313800002], [2.93995, 2.984075, 12.395404313800002]], 'spacegroup_kinds': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139], 'spacegroup': {'number': 1, 'setting': 1}, 'unit_cell': 'conventional', 'occupancy': {'0': {'H': 1.0}, '1': {'H': 1.0}, '2': {'H': 1.0}, '3': {'H': 1.0}, '4': {'H': 1.0}, '5': {'H': 1.0}, '6': {'H': 1.0}, '7': {'H': 1.0}, '8': {'H': 1.0}, '9': {'H': 1.0}, '10': {'H': 1.0}, '11': {'H': 1.0}, '12': {'H': 1.0}, '13': {'H': 1.0}, '14': {'H': 1.0}, '15': {'H': 1.0}, '16': {'H': 1.0}, '17': {'H': 1.0}, '18': {'H': 1.0}, '19': {'H': 1.0}, '20': {'H': 1.0}, '21': {'H': 1.0}, '22': {'H': 1.0}, '23': {'H': 1.0}, '24': {'H': 1.0}, '25': {'H': 1.0}, '26': {'H': 1.0}, '27': {'H': 1.0}, '28': {'H': 1.0}, '29': {'H': 1.0}, '30': {'H': 1.0}, '31': {'H': 1.0}, '32': {'H': 1.0}, '33': {'H': 1.0}, '34': {'H': 1.0}, '35': {'H': 1.0}, '36': {'H': 1.0}, '37': {'H': 1.0}, '38': {'H': 1.0}, '39': {'H': 1.0}, '40': {'H': 1.0}, '41': {'H': 1.0}, '42': {'H': 1.0}, '43': {'H': 1.0}, '44': {'H': 1.0}, '45': {'H': 1.0}, '46': {'H': 1.0}, '47': {'H': 1.0}, '48': {'H': 1.0}, '49': {'H': 1.0}, '50': {'H': 1.0}, '51': {'H': 1.0}, '52': {'H': 1.0}, '53': {'H': 1.0}, '54': {'H': 1.0}, '55': {'H': 1.0}, '56': {'H': 1.0}, '57': {'H': 1.0}, '58': {'H': 1.0}, '59': {'H': 1.0}, '60': {'H': 1.0}, '61': {'H': 1.0}, '62': {'H': 1.0}, '63': {'H': 1.0}, '64': {'C': 1.0}, '65': {'C': 1.0}, '66': {'C': 1.0}, '67': {'C': 1.0}, '68': {'C': 1.0}, '69': {'C': 1.0}, '70': {'C': 1.0}, '71': {'C': 1.0}, '72': {'C': 1.0}, '73': {'C': 1.0}, '74': {'C': 1.0}, '75': {'C': 1.0}, '76': {'C': 1.0}, '77': {'C': 1.0}, '78': {'C': 1.0}, '79': {'C': 1.0}, '80': {'C': 1.0}, '81': {'C': 1.0}, '82': {'C': 1.0}, '83': {'C': 1.0}, '84': {'C': 1.0}, '85': {'C': 1.0}, '86': {'C': 1.0}, '87': {'C': 1.0}, '88': {'C': 1.0}, '89': {'C': 1.0}, '90': {'C': 1.0}, '91': {'C': 1.0}, '92': {'C': 1.0}, '93': {'C': 1.0}, '94': {'C': 1.0}, '95': {'C': 1.0}, '96': {'C': 1.0}, '97': {'C': 1.0}, '98': {'C': 1.0}, '99': {'C': 1.0}, '100': {'C': 1.0}, '101': {'C': 1.0}, '102': {'C': 1.0}, '103': {'C': 1.0}, '104': {'C': 1.0}, '105': {'C': 1.0}, '106': {'C': 1.0}, '107': {'C': 1.0}, '108': {'C': 1.0}, '109': {'C': 1.0}, '110': {'C': 1.0}, '111': {'C': 1.0}, '112': {'N': 1.0}, '113': {'N': 1.0}, '114': {'N': 1.0}, '115': {'N': 1.0}, '116': {'N': 1.0}, '117': {'N': 1.0}, '118': {'N': 1.0}, '119': {'N': 1.0}, '120': {'N': 1.0}, '121': {'N': 1.0}, '122': {'N': 1.0}, '123': {'N': 1.0}, '124': {'N': 1.0}, '125': {'N': 1.0}, '126': {'N': 1.0}, '127': {'N': 1.0}, '128': {'N': 1.0}, '129': {'N': 1.0}, '130': {'N': 1.0}, '131': {'N': 1.0}, '132': {'N': 1.0}, '133': {'N': 1.0}, '134': {'N': 1.0}, '135': {'N': 1.0}, '136': {'Fe': 1.0}, '137': {'Fe': 1.0}, '138': {'Fe': 1.0}, '139': {'Fe': 1.0}}, 'MOF_name': 'EWIKAX03', 'Dos at Fermi energy': 29.8688, 'Band_gap': 0.004466204, 'Dos at VBM': 29.8688, 'Dos at CBM': 29.8688, 'HSE band gap': 'not found', 'LCD': '4.36078', 'PLD': '1.82778', 'Density': '1.696', 'Accessible Surface Area': '0', 'Volume Fraction': '0.51294', 'Criteria#': 2, 'Multiplier_Sum': '1', 'Space_group#': '14', 'Space_group': 'P21/c', 'Temp': '94', 'Zprime': '1', 'Year': '2008', 'Metal': 'Fe', 'Metals number': '1', 'Cell volume': '1954.407', 'Metal density': '0.000511664', 'Crit: metal': 'yes', 'Crit: redox match': 'yes', 'Crit: redox active linker': 'no', 'Crit: pi-pi stacking': 'no', 'without ions': 'no', '2D': 'no', '1aromatico-up': 'yes', '2aromatici-up': 'no', 'N3--NCN up': 'no', 'benzene': 'no', '6m-rings': 'yes', 'pyridine': 'no', 'pyrimidine': 'no', 'metal-S': 'no', 'metal-O': 'no', 'metal-N': 'yes', 'metal-halogen': 'no', '5m-ring-leg2met': 'no', '5-m-rings': 'no', 'CN-M': 'yes', 'M-h2o': 'no', 'M-H2O-M': 'no', 'M-N-NM-N-M': 'no', 'M-C-C-TRIANG': 'no', 'COOM': 'no', 'units': {'Dos at Fermi energy': 'eln/cell', 'Dos at VBM': 'eln/cell', 'Dos at CBM': 'eln/cell', 'Density': 'g/cm3', 'Accessible Surface Area': 'm2/g'}, 'volume': 1954.406783203316, 'elements': {'1': 64, '6': 48, '7': 24, '26': 4}, 'username': 'ubuntu', 'uploaded': '2023-09-01T18:13:24.859685', 'modified': '2023-09-01T18:13:24.859694', 'hash_structure': '660d3f56483cf3a6dd94d833d4478fcf', 'hash': '2c1caa8af0562303fc8cfe0eba64b444', 'derived': {'arrays_keys': ['numbers', 'spacegroup_kinds', 'positions'], 'info_keys': ['pbc', 'formula', 'occupancy', 'cell', 'unit_cell', 'spacegroup', 'n_atoms', 'MOF_name', 'Dos at Fermi energy', 'Band_gap', 'Dos at VBM', 'Dos at CBM', 'HSE band gap', 'LCD', 'PLD', 'Density', 'Accessible Surface Area', 'Volume Fraction', 'Criteria#', 'Multiplier_Sum', 'Space_group#', 'Space_group', 'Temp', 'Zprime', 'Year', 'Metal', 'Metals number', 'Cell volume', 'Metal density', 'Crit: metal', 'Crit: redox match', 'Crit: redox active linker', 'Crit: pi-pi stacking', 'without ions', '2D', '1aromatico-up', '2aromatici-up', 'N3--NCN up', 'benzene', '6m-rings', 'pyridine', 'pyrimidine', 'metal-S', 'metal-O', 'metal-N', 'metal-halogen', '5m-ring-leg2met', '5-m-rings', 'CN-M', 'M-h2o', 'M-H2O-M', 'M-N-NM-N-M', 'M-C-C-TRIANG', 'COOM', 'units', 'volume'], 'results_keys': [], 'derived_keys': ['elements', 'username', 'uploaded', 'modified', 'volume', 'hash_structure', 'hash']}}\n", + "dict_keys(['_id', 'n_atoms', 'cell', 'pbc', 'formula', 'numbers', 'positions', 'spacegroup_kinds', 'spacegroup', 'unit_cell', 'occupancy', 'MOF_name', 'Dos at Fermi energy', 'Band_gap', 'Dos at VBM', 'Dos at CBM', 'HSE band gap', 'LCD', 'PLD', 'Density', 'Accessible Surface Area', 'Volume Fraction', 'Criteria#', 'Multiplier_Sum', 'Space_group#', 'Space_group', 'Temp', 'Zprime', 'Year', 'Metal', 'Metals number', 'Cell volume', 'Metal density', 'Crit: metal', 'Crit: redox match', 'Crit: redox active linker', 'Crit: pi-pi stacking', 'without ions', '2D', '1aromatico-up', '2aromatici-up', 'N3--NCN up', 'benzene', '6m-rings', 'pyridine', 'pyrimidine', 'metal-S', 'metal-O', 'metal-N', 'metal-halogen', '5m-ring-leg2met', '5-m-rings', 'CN-M', 'M-h2o', 'M-H2O-M', 'M-N-NM-N-M', 'M-C-C-TRIANG', 'COOM', 'units', 'volume', 'elements', 'username', 'uploaded', 'modified', 'hash_structure', 'hash', 'derived'])\n" + ] + } + ], + "source": [ + "query = 'MOF_name: *'\n", + "print(len(list(abcd.get_items(query))))\n", + "print(list(abcd.get_items(query))[0])\n", + "print(list(abcd.get_items(query))[0].keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "query = 'MOF_name: *'\n", + "abcd.add_property(\n", + " data={\"example_property\": \"example_value\"},\n", + " query=query\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['example_value']" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abcd.refresh()\n", + "abcd.property(\"example_property\", query)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.rename_property(\n", + " name=\"example_property\",\n", + " new_name=\"renamed_property\",\n", + " query=query\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['example_value']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abcd.refresh()\n", + "abcd.property(\"renamed_property\", query)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.delete_property(\n", + " name=\"renamed_property\",\n", + " query=query\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abcd.refresh()\n", + "# abcd.property(\"example_property\", query)\n", + "abcd.property(\"renamed_property\", query)" + ] + } + ], + "metadata": { + "@webio": { + "lastCommId": null, + "lastKernelId": null + }, + "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.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/abcd_opensearch_queries.ipynb b/tutorials/abcd_opensearch_queries.ipynb new file mode 100644 index 00000000..507289d7 --- /dev/null +++ b/tutorials/abcd_opensearch_queries.ipynb @@ -0,0 +1,1143 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "# Usage of ABCD database with extra information" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "pycharm": { + "is_executing": false + } + }, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "pycharm": { + "is_executing": false + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from abcd import ABCD\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "First of all, we need to define the url of the database. It could be local or remote:\n", + "\n", + "- direct access: url = 'opensearch://admin:admin@localhost:9200'\n", + "- api access: url = 'http://localhost/api'\n", + "\n", + "using with statement to catch the riased exceptions. You may can ignore them but in that case need to handle all the unexpected events. (cannot connect to db, lost connection, wrong filter, wrong url, etc. )" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "pycharm": { + "is_executing": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenSearchDatabase(url=localhost:9200, index=atoms) \n" + ] + } + ], + "source": [ + "url = 'opensearch://admin:myStrongPassword123!@localhost:9200'\n", + "abcd = ABCD.from_url(url)\n", + "\n", + "print(abcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 2055\n", + " type: opensearch\n" + ] + } + ], + "source": [ + "abcd.print_info()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "## Cleanup \n", + "\n", + "WARNING!! Remove all elements from the database.\n", + "Only supported in the case of local access" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.destroy()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.create()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================ ABCD OpenSearch =================\n", + " type: opensearch\n", + " host: localhost\n", + " port: 9200\n", + " db: abcd\n", + " index: atoms\n", + "number of confs: 0\n", + " type: opensearch\n" + ] + } + ], + "source": [ + "abcd.print_info()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "pycharm": {} + }, + "source": [ + "## Uploading configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/ubuntu/abcd/tutorials\n" + ] + } + ], + "source": [ + "from ase.io import iread\n", + "!pwd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Data can be entered into the database as ASE Atoms objects, allowing any format readable by ase.io.read to be used." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "directory = Path('/home/ubuntu/data/')\n", + "file = directory / 'input.data.2055.xyz'" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 12.6 s, sys: 152 ms, total: 12.7 s\n", + "Wall time: 18.8 s\n" + ] + } + ], + "source": [ + "%%time\n", + "with abcd as db:\n", + " for atoms in iread(file.as_posix(), index=slice(None)):\n", + " db.push(atoms, store_calc=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "abcd.refresh()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example queries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Text queries" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n", + "316\n" + ] + } + ], + "source": [ + "# Explicit queries via dictionaries\n", + "\n", + "query = {\"match_all\": {}}\n", + "print(abcd.count(query))\n", + "query = {\n", + " \"match\": {\n", + " \"n_atoms\": 114\n", + " }\n", + "}\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n" + ] + } + ], + "source": [ + "# Basic text\n", + "\n", + "query = 'ubuntu'\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n", + "0\n" + ] + } + ], + "source": [ + "# Query specific fields\n", + "\n", + "query = 'username:ubuntu'\n", + "print(abcd.count(query))\n", + "\n", + "query = 'formula:ubuntu'\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n" + ] + } + ], + "source": [ + "# Range\n", + "\n", + "query = 'username:[ubunta TO ubuntx]'\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n", + "2055\n" + ] + } + ], + "source": [ + "# Wildcards\n", + "\n", + "query = 'ubu?tu'\n", + "print(abcd.count(query))\n", + "\n", + "query = 'username: *'\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "316\n", + "2055\n" + ] + } + ], + "source": [ + "# Logical combinations\n", + "\n", + "query = 'username:[ubunta TO ubuntx] AND formula: C48H28O32Zr6'\n", + "print(abcd.count(query))\n", + "\n", + "query = 'username:[ubunta TO ubuntx] OR formula: C48H28O32Zr6'\n", + "print(abcd.count(query))" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2055\n", + "316\n", + "C48H28O32Zr6\n" + ] + } + ], + "source": [ + "# Regex - wrap with `/`\n", + "\n", + "query = '/u.untu/'\n", + "print(abcd.count(query))\n", + "\n", + "# Search for (something like) C48H28O32Zr6\n", + "# Note: anchored by default, so cannot use ^ and $\n", + "query = 'formula: /C.\\d[G-I]28O32Z\\w[^7]/'\n", + "print(abcd.count(query))\n", + "\n", + "for prop in abcd.property(\"formula\", query):\n", + " print(prop)\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Numerical queries" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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SoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArtWnuDgAA0Nr1fHbjdWtOL06/BT2xC2dyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICV+MRj4O/4xFEAsAtncgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArMQt5ACAVoePjGgdOJMDAACs1OghZ+HChQoJCQlY+vXr57RfunRJmZmZ6tKlizp27KiMjAwVFxc3djcAAEAr1yRncu655x6dO3fOWT788EOnbc6cOXr33Xe1bt067dq1S2fPntUjjzzSFN0AAACtWJNck9OmTRt5PJ5a28vLy/WrX/1Kubm5euCBByRJq1atUv/+/ZWfn68RI0Y0RXcAAEAr1CRncj799FPFx8erd+/emjRpkoqKiiRJBQUFunz5slJSUpzafv36KTExUXl5efXur6KiQn6/P2ABAAC4lkYPOcnJyVq9erU2b96sFStW6NSpU/rmN7+p8+fPy+fzKTw8XFFRUQGPiY2Nlc/nq3efOTk5ioyMdJaEhITG7jYAALBMo79dlZaW5vw8aNAgJScnq0ePHvrtb3+r9u3b39A+s7OzlZWV5az7/X6CDoBGwa3EgL2a/BbyqKgo3X333Tpx4oQ8Ho8qKytVVlYWUFNcXFznNTxXuFwuud3ugAUAAOBamjzkXLhwQSdPnlRcXJyGDRumtm3bavv27U778ePHVVRUJK/X29RdAQAArUijv131r//6r3rwwQfVo0cPnT17Vs8995zCwsI0ceJERUZGatq0acrKylJ0dLTcbreefPJJeb1e7qwCAACNqtFDzmeffaaJEyfq888/V7du3TRy5Ejl5+erW7dukqSlS5cqNDRUGRkZqqioUGpqql599dXG7gYAAGjlGj3krF279prt7dq10/Lly7V8+fLGfmoAAAAH310FAACsxLeQA82A25YBoOlxJgcAAFiJMzkAcBvhLB/QeAg5CMAvWACALXi7CgAAWImQAwAArMTbVQDQwvC2MtAwnMkBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASt5ADQCvFreiwHSEHAIA6EAJbPkJOM+NFBABA0yDkAACs0pB/PKJ14MJjAABgJc7koMXjLT8AQF0IOQBgId6yAQg5QKO7lX9cOIsFNC9eg7c3Qs4NYmIDAHB7I+QAAFoM3oZDMAg5aDacDQNwNQIMGhshB7e1xvqlxy9PAGh9CDmA5Qh4AForQk4rwh87tDbM+VuDt55xu+ITjwEAgJU4k4MmYeu/oG09rpaoJf6/aIl9BloyzuQAAAArcSYHANDkWvNZrNZ87M2NkNMC3G4X9fGCBWrjdQHcfgg5AIB6Ed7QkhFymhC/HHAzmD8AcHO48BgAAFiJMzmW4F/9QNPh9QW0TM16Jmf58uXq2bOn2rVrp+TkZH388cfN2R0AAGCRZgs5b775prKysvTcc8/pD3/4gwYPHqzU1FSVlJQ0V5cAAIBFmi3k/OxnP9P06dP1xBNPKCkpSStXrlRERIRee+215uoSAACwSLNck1NZWamCggJlZ2c720JDQ5WSkqK8vLxa9RUVFaqoqHDWy8vLJUl+v79J+ldT8UWT7BdoyRrr9cbrC7gxjfk3ryGvw6b4G3tln8aYRt93XZol5Pztb39TdXW1YmNjA7bHxsbq2LFjtepzcnL0/PPP19qekJDQZH0EECjylebuAdC63erXYFM+3/nz5xUZGdl0T/B3LeLuquzsbGVlZTnrNTU1Ki0tVZcuXRQSEtKMPaub3+9XQkKCzpw5I7fb3dzdua0xVg3DODUM49RwjFXDME4N09BxMsbo/Pnzio+PvyX9apaQ07VrV4WFham4uDhge3FxsTweT616l8sll8sVsC0qKqopu9go3G43L4oGYqwahnFqGMap4RirhmGcGqYh43QrzuBc0SwXHoeHh2vYsGHavn27s62mpkbbt2+X1+ttji4BAADLNNvbVVlZWZo6daqGDx+u++67T6+88oouXryoJ554orm6BAAALNJsIefRRx/VX//6Vy1YsEA+n09DhgzR5s2ba12M3BK5XC4999xztd5iQ22MVcMwTg3DODUcY9UwjFPD3K7jFGJu1X1cAAAAtxBf0AkAAKxEyAEAAFYi5AAAACsRcgAAgJUIOdexceNGJScnq3379urcubMmTJgQ0F5UVKT09HRFREQoJiZGc+fOVVVVVUDNzp079Q//8A9yuVzq06ePVq9eXet5li9frp49e6pdu3ZKTk7Wxx9/HNB+6dIlZWZmqkuXLurYsaMyMjJqfZhic+nZs6dCQkIClsWLFzvtp0+frtUeEhKi/Pz8gP2sW7dO/fr1U7t27TRw4EBt2rQpoN0YowULFiguLk7t27dXSkqKPv3004Ca0tJSTZo0SW63W1FRUZo2bZouXLjQdAcfpOuNlSQdPHhQ3/zmN9WuXTslJCRoyZIltfbTGsZK+up764YMGaKQkBAdOHDA2c6cClTfOEnMpyseeughJSYmql27doqLi9PkyZN19uxZp5059ZXrjZPUwuaUQb3eeust07lzZ7NixQpz/PhxU1hYaN58802nvaqqygwYMMCkpKSY/fv3m02bNpmuXbua7Oxsp+ZPf/qTiYiIMFlZWebIkSNm2bJlJiwszGzevNmpWbt2rQkPDzevvfaaKSwsNNOnTzdRUVGmuLjYqZk5c6ZJSEgw27dvN/v27TMjRoww999//60ZiOvo0aOHWbRokTl37pyzXLhwwWk/deqUkWS2bdsWUFNZWenUfPTRRyYsLMwsWbLEHDlyxMyfP9+0bdvWHDp0yKlZvHixiYyMNO+884754x//aB566CHTq1cv8+WXXzo148aNM4MHDzb5+fnm97//venTp4+ZOHHirRmIBrjeWJWXl5vY2FgzadIkc/jwYfPGG2+Y9u3bm1/+8pdOTWsZK2OM+dGPfmTS0tKMJLN//35nO3MqUH3jxHz6/372s5+ZvLw8c/r0afPRRx8Zr9drvF6v086c+sr1xqmlzSlCTj0uX75s7rjjDvPf//3f9dZs2rTJhIaGGp/P52xbsWKFcbvdpqKiwhhjzDPPPGPuueeegMc9+uijJjU11Vm/7777TGZmprNeXV1t4uPjTU5OjjHGmLKyMtO2bVuzbt06p+bo0aNGksnLy7u5A20EPXr0MEuXLq23/covj6t/+X7d9773PZOenh6wLTk52fzwhz80xhhTU1NjPB6Peemll5z2srIy43K5zBtvvGGMMebIkSNGkvnkk0+cmvfee8+EhISYv/zlLzdwZI3vemP16quvms6dOzvzxxhj5s2bZ/r27eust5ax2rRpk+nXr58pLCysN+Qwp649Tsyn+q1fv96EhIQ4IYY5Vbevj1NLm1O8XVWPP/zhD/rLX/6i0NBQDR06VHFxcUpLS9Phw4edmry8PA0cODDgAwxTU1Pl9/tVWFjo1KSkpATsOzU1VXl5eZKkyspKFRQUBNSEhoYqJSXFqSkoKNDly5cDavr166fExESnprktXrxYXbp00dCh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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# All energies\n", + "\n", + "data = abcd.property('energy')\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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SoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArtWnuDgAA0Nr1fHbjdWtOL06/BT2xC2dyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICV+MRj4O/4xFEAsAtncgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArMQt5ACAVoePjGgdOJMDAACs1OghZ+HChQoJCQlY+vXr57RfunRJmZmZ6tKlizp27KiMjAwVFxc3djcAAEAr1yRncu655x6dO3fOWT788EOnbc6cOXr33Xe1bt067dq1S2fPntUjjzzSFN0AAACtWJNck9OmTRt5PJ5a28vLy/WrX/1Kubm5euCBByRJq1atUv/+/ZWfn68RI0Y0RXcAAEAr1CRncj799FPFx8erd+/emjRpkoqKiiRJBQUFunz5slJSUpzafv36KTExUXl5efXur6KiQn6/P2ABAAC4lkYPOcnJyVq9erU2b96sFStW6NSpU/rmN7+p8+fPy+fzKTw8XFFRUQGPiY2Nlc/nq3efOTk5ioyMdJaEhITG7jYAALBMo79dlZaW5vw8aNAgJScnq0ePHvrtb3+r9u3b39A+s7OzlZWV5az7/X6CDoBGwa3EgL2a/BbyqKgo3X333Tpx4oQ8Ho8qKytVVlYWUFNcXFznNTxXuFwuud3ugAUAAOBamjzkXLhwQSdPnlRcXJyGDRumtm3bavv27U778ePHVVRUJK/X29RdAQAArUijv131r//6r3rwwQfVo0cPnT17Vs8995zCwsI0ceJERUZGatq0acrKylJ0dLTcbreefPJJeb1e7qwCAACNqtFDzmeffaaJEyfq888/V7du3TRy5Ejl5+erW7dukqSlS5cqNDRUGRkZqqioUGpqql599dXG7gYAAGjlGj3krF279prt7dq10/Lly7V8+fLGfmoAAAAH310FAACsxLeQA82A25YBoOlxJgcAAFiJMzkAcBvhLB/QeAg5CMAvWACALXi7CgAAWImQAwAArMTbVQDQwvC2MtAwnMkBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASt5ADQCvFreiwHSEHAIA6EAJbPkJOM+NFBABA0yDkAACs0pB/PKJ14MJjAABgJc7koMXjLT8AQF0IOQBgId6yAQg5QKO7lX9cOIsFNC9eg7c3Qs4NYmIDAHB7I+QAAFoM3oZDMAg5aDacDQNwNQIMGhshB7e1xvqlxy9PAGh9CDmA5Qh4AForQk4rwh87tDbM+VuDt55xu+ITjwEAgJU4k4MmYeu/oG09rpaoJf6/aIl9BloyzuQAAAArcSYHANDkWvNZrNZ87M2NkNMC3G4X9fGCBWrjdQHcfgg5AIB6Ed7QkhFymhC/HHAzmD8AcHO48BgAAFiJMzmW4F/9QNPh9QW0TM16Jmf58uXq2bOn2rVrp+TkZH388cfN2R0AAGCRZgs5b775prKysvTcc8/pD3/4gwYPHqzU1FSVlJQ0V5cAAIBFmi3k/OxnP9P06dP1xBNPKCkpSStXrlRERIRee+215uoSAACwSLNck1NZWamCggJlZ2c720JDQ5WSkqK8vLxa9RUVFaqoqHDWy8vLJUl+v79J+ldT8UWT7BdoyRrr9cbrC7gxjfk3ryGvw6b4G3tln8aYRt93XZol5Pztb39TdXW1YmNjA7bHxsbq2LFjtepzcnL0/PPP19qekJDQZH0EECjylebuAdC63erXYFM+3/nz5xUZGdl0T/B3LeLuquzsbGVlZTnrNTU1Ki0tVZcuXRQSEtKMPaub3+9XQkKCzpw5I7fb3dzdua0xVg3DODUM49RwjFXDME4N09BxMsbo/Pnzio+PvyX9apaQ07VrV4WFham4uDhge3FxsTweT616l8sll8sVsC0qKqopu9go3G43L4oGYqwahnFqGMap4RirhmGcGqYh43QrzuBc0SwXHoeHh2vYsGHavn27s62mpkbbt2+X1+ttji4BAADLNNvbVVlZWZo6daqGDx+u++67T6+88oouXryoJ554orm6BAAALNJsIefRRx/VX//6Vy1YsEA+n09DhgzR5s2ba12M3BK5XC4999xztd5iQ22MVcMwTg3DODUcY9UwjFPD3K7jFGJu1X1cAAAAtxBf0AkAAKxEyAEAAFYi5AAAACsRcgAAgJUIOdexceNGJScnq3379urcubMmTJgQ0F5UVKT09HRFREQoJiZGc+fOVVVVVUDNzp079Q//8A9yuVzq06ePVq9eXet5li9frp49e6pdu3ZKTk7Wxx9/HNB+6dIlZWZmqkuXLurYsaMyMjJqfZhic+nZs6dCQkIClsWLFzvtp0+frtUeEhKi/Pz8gP2sW7dO/fr1U7t27TRw4EBt2rQpoN0YowULFiguLk7t27dXSkqKPv3004Ca0tJSTZo0SW63W1FRUZo2bZouXLjQdAcfpOuNlSQdPHhQ3/zmN9WuXTslJCRoyZIltfbTGsZK+up764YMGaKQkBAdOHDA2c6cClTfOEnMpyseeughJSYmql27doqLi9PkyZN19uxZp5059ZXrjZPUwuaUQb3eeust07lzZ7NixQpz/PhxU1hYaN58802nvaqqygwYMMCkpKSY/fv3m02bNpmuXbua7Oxsp+ZPf/qTiYiIMFlZWebIkSNm2bJlJiwszGzevNmpWbt2rQkPDzevvfaaKSwsNNOnTzdRUVGmuLjYqZk5c6ZJSEgw27dvN/v27TMjRoww999//60ZiOvo0aOHWbRokTl37pyzXLhwwWk/deqUkWS2bdsWUFNZWenUfPTRRyYsLMwsWbLEHDlyxMyfP9+0bdvWHDp0yKlZvHixiYyMNO+884754x//aB566CHTq1cv8+WXXzo148aNM4MHDzb5+fnm97//venTp4+ZOHHirRmIBrjeWJWXl5vY2FgzadIkc/jwYfPGG2+Y9u3bm1/+8pdOTWsZK2OM+dGPfmTS0tKMJLN//35nO3MqUH3jxHz6/372s5+ZvLw8c/r0afPRRx8Zr9drvF6v086c+sr1xqmlzSlCTj0uX75s7rjjDvPf//3f9dZs2rTJhIaGGp/P52xbsWKFcbvdpqKiwhhjzDPPPGPuueeegMc9+uijJjU11Vm/7777TGZmprNeXV1t4uPjTU5OjjHGmLKyMtO2bVuzbt06p+bo0aNGksnLy7u5A20EPXr0MEuXLq23/covj6t/+X7d9773PZOenh6wLTk52fzwhz80xhhTU1NjPB6Peemll5z2srIy43K5zBtvvGGMMebIkSNGkvnkk0+cmvfee8+EhISYv/zlLzdwZI3vemP16quvms6dOzvzxxhj5s2bZ/r27eust5ax2rRpk+nXr58pLCysN+Qwp649Tsyn+q1fv96EhIQ4IYY5Vbevj1NLm1O8XVWPP/zhD/rLX/6i0NBQDR06VHFxcUpLS9Phw4edmry8PA0cODDgAwxTU1Pl9/tVWFjo1KSkpATsOzU1VXl5eZKkyspKFRQUBNSEhoYqJSXFqSkoKNDly5cDavr166fExESnprktXrxYXbp00dCh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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# No lower bound\n", + "\n", + "query = \"energy: [* TO -30000]\"\n", + "data = abcd.property('energy', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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VwMAAFBLYYWggoIC5eTkaO/evdq2bZuuXbumESNG6PLly06fWbNmaePGjVq3bp0KCgp05swZPfXUU0779evXlZmZqatXr2rPnj16++23tXr1as2bN6/+qgIAAKhBhDHG3O3G586dU0JCggoKCjR06FAFAgF16tRJ+fn5evrppyVJX375pfr06aPCwkINHjxYmzdv1pgxY3TmzBklJiZKklatWqU5c+bo3Llzio6OrvF5Kyoq5PF4FAgE5Ha773b4aEDdXvywxj6nF2feg5EAAL6psV6jm9rf7zpdExQIBCRJ8fHxkqSioiJdu3ZN6enpTp/evXurS5cuKiwslCQVFhaqb9++TgCSpIyMDFVUVOjIkSPVPk8wGFRFRUXIAgAAUBd3HYKqqqr0/PPP61vf+pYeeughSZLf71d0dLTi4uJC+iYmJsrv9zt9bg5AN9pvtFVn0aJF8ng8zpKSknK3wwYAAJBUhxCUk5Ojw4cPa+3atfU5nmrl5eUpEAg4S0lJSYM/JwAAaNla3c1GM2fO1KZNm7R792517tzZWe/1enX16lVduHAh5GxQaWmpvF6v0+ezzz4L2d+Nu8du9Pkml8sll8t1N0MFAACoVlhngowxmjlzptavX68dO3aoe/fuIe0DBw5U69attX37dmfd8ePHVVxcLJ/PJ0ny+Xw6dOiQysrKnD7btm2T2+1WampqXWoBAACotbDOBOXk5Cg/P18bNmxQ+/btnWt4PB6P2rRpI4/HoylTpig3N1fx8fFyu9167rnn5PP5NHjwYEnSiBEjlJqaqokTJ2rJkiXy+/2aO3eucnJyONsDAADumbBC0Ouvvy5JGjZsWMj6t956S88++6wkadmyZYqMjFRWVpaCwaAyMjL02muvOX2joqK0adMmzZgxQz6fT23btlV2drYWLlxYt0oAAADCEFYIqs1HCsXExGjlypVauXLlbft07dpVH330UThPDQAAUK/47jAAAGAlQhAAALASIQgAAFiJEAQAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKhCAAAGAlQhAAALASIQgAAFiJEAQAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwUtghaPfu3Ro7dqySk5MVERGhDz74IKT92WefVURERMgycuTIkD7l5eWaMGGC3G634uLiNGXKFF26dKlOhQAAAIQj7BB0+fJlPfzww1q5cuVt+4wcOVJnz551lnfffTekfcKECTpy5Ii2bdumTZs2affu3Zo2bVr4owcAALhLrcLdYNSoURo1atQd+7hcLnm93mrbjh07pi1btmj//v0aNGiQJGnFihUaPXq0Xn31VSUnJ4c7JAAAgLA1yDVBu3btUkJCgnr16qUZM2bo/PnzTlthYaHi4uKcACRJ6enpioyM1L59+6rdXzAYVEVFRcgCAABQF/UegkaOHKnf/OY32r59u/75n/9ZBQUFGjVqlK5fvy5J8vv9SkhICNmmVatWio+Pl9/vr3afixYtksfjcZaUlJT6HjYAALBM2G+H1WT8+PHOz3379lW/fv3Uo0cP7dq1S8OHD7+rfebl5Sk3N9d5XFFRQRACAAB10uC3yN93333q2LGjTpw4IUnyer0qKysL6VNZWany8vLbXkfkcrnkdrtDFgAAgLpo8BD09ddf6/z580pKSpIk+Xw+XbhwQUVFRU6fHTt2qKqqSmlpaQ09HAAAAEl38XbYpUuXnLM6knTq1CkdOHBA8fHxio+P14IFC5SVlSWv16uTJ0/qhRde0P3336+MjAxJUp8+fTRy5EhNnTpVq1at0rVr1zRz5kyNHz+eO8MAAMA9E/aZoM8//1wDBgzQgAEDJEm5ubkaMGCA5s2bp6ioKB08eFBPPPGEevbsqSlTpmjgwIH6wx/+IJfL5exjzZo16t27t4YPH67Ro0dryJAh+td//df6qwoAAKAGYZ8JGjZsmIwxt23funVrjfuIj49Xfn5+uE8NAABQb/juMAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKhCAAAGAlQhAAALASIQgAAFiJEAQAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKhCAAAGClsEPQ7t27NXbsWCUnJysiIkIffPBBSLsxRvPmzVNSUpLatGmj9PR0ffXVVyF9ysvLNWHCBLndbsXFxWnKlCm6dOlSnQoBAAAIR9gh6PLly3r44Ye1cuXKatuXLFmiX/3qV1q1apX27duntm3bKiMjQ1euXHH6TJgwQUeOHNG2bdu0adMm7d69W9OmTbv7KgAAAMLUKtwNRo0apVGjRlXbZozR8uXLNXfuXI0bN06S9Jvf/EaJiYn64IMPNH78eB07dkxbtmzR/v37NWjQIEnSihUrNHr0aL366qtKTk6uQzkAAAC1U6/XBJ06dUp+v1/p6enOOo/Ho7S0NBUWFkqSCgsLFRcX5wQgSUpPT1dkZKT27dtXn8MBAAC4rbDPBN2J3++XJCUmJoasT0xMdNr8fr8SEhJCB9GqleLj450+3xQMBhUMBp3HFRUV9TlsAABgoWZxd9iiRYvk8XicJSUlpbGHBAAAmrl6DUFer1eSVFpaGrK+tLTUafN6vSorKwtpr6ysVHl5udPnm/Ly8hQIBJylpKSkPocNAAAsVK8hqHv37vJ6vdq+fbuzrqKiQvv27ZPP55Mk+Xw+XbhwQUVFRU6fHTt2qKqqSmlpadXu1+Vyye12hywAAAB1EfY1QZcuXdKJEyecx6dOndKBAwcUHx+vLl266Pnnn9c//dM/6YEHHlD37t310ksvKTk5WU8++aQkqU+fPho5cqSmTp2qVatW6dq1a5o5c6bGjx/PnWEAAOCeCTsEff755/rOd77jPM7NzZUkZWdna/Xq1XrhhRd0+fJlTZs2TRcuXNCQIUO0ZcsWxcTEONusWbNGM2fO1PDhwxUZGamsrCz96le/qodyAAAAaifCGGMaexDhqqiokMfjUSAQ4K2xJqrbix/W2Of04sx7MBIAwDc11mt0U/v73SzuDgMAAKhvhCAAAGAlQhAAALASIQgAAFiJEAQAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKhCAAAGAlQhAAALASIQgAAFiJEAQAAKzUqrEHgPrR7cUPa+xzenHmPRgJAADNA2eCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKfG0GAAB3ia8sat44EwQAAKxU7yHo5ZdfVkRERMjSu3dvp/3KlSvKyclRhw4d1K5dO2VlZam0tLS+hwEAAHBHDXIm6MEHH9TZs2ed5ZNPPnHaZs2apY0bN2rdunUqKCjQmTNn9NRTTzXEMAAAAG6rQa4JatWqlbxe7y3rA4GA/v3f/135+fn67ne/K0l666231KdPH+3du1eDBw9uiOEAAADcokHOBH311VdKTk7WfffdpwkTJqi4uFiSVFRUpGvXrik9Pd3p27t3b3Xp0kWFhYUNMRQAAIBq1fuZoLS0NK1evVq9evXS2bNntWDBAj3++OM6fPiw/H6/oqOjFRcXF7JNYmKi/H7/bfcZDAYVDAadxxUVFfU9bAAAYJl6D0GjRo1yfu7Xr5/S0tLUtWtXvffee2rTps1d7XPRokVasGBBfQ0RAACg4W+Rj4uLU8+ePXXixAl5vV5dvXpVFy5cCOlTWlpa7TVEN+Tl5SkQCDhLSUlJA48aAAC0dA0egi5duqSTJ08qKSlJAwcOVOvWrbV9+3an/fjx4youLpbP57vtPlwul9xud8gCAABQF/X+dtg//MM/aOzYseratavOnDmj+fPnKyoqSs8884w8Ho+mTJmi3NxcxcfHy+1267nnnpPP5+POMAAAcE/Vewj6+uuv9cwzz+j8+fPq1KmThgwZor1796pTp06SpGXLlikyMlJZWVkKBoPKyMjQa6+9Vt/DAAAAuKN6D0Fr1669Y3tMTIxWrlyplStX1vdTAwAA1BrfHQYAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWKnePywRgL26vfhhjX1OL868ByMBgJpxJggAAFiJEAQAAKxECAIAAFYiBAEAACtxYTQAoMFx0TyaIs4EAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEneHAfWMu2CAu8Pcwb1GCAJQK7X5A4WWh2CClowQBACoEwIymitCEPD/8S9eALALIQjAPUXYBNBUEILuEi/kANA08fqM2uIWeQAAYCXOBDUy/sUCoCHw2gLUjBAEAEA1uOut5SMEoUnjX7MAgIZCCGpA/CsCAICmixAEKxBI74zfz51xRhJombg7DAAAWIkzQQCapfo6e8UZHMBehCA0eza/lcPbNABw9whBzYDNf+QB3IrXBKB+EIIA4B7hzF3TQZCERAgCmixepAGgYRGCAFiNsAnYixBUDV4UAQBo+ficIAAAYCXOBAGNgLONuB3+3wDuHc4EAQAAKzVqCFq5cqW6deummJgYpaWl6bPPPmvM4QAAAIs0Wgj67W9/q9zcXM2fP19//OMf9fDDDysjI0NlZWWNNSQAAGCRRgtBv/jFLzR16lRNnjxZqampWrVqlWJjY/Xmm2821pAAAIBFGuXC6KtXr6qoqEh5eXnOusjISKWnp6uwsPCW/sFgUMFg0HkcCAQkSRUVFQ0yvqrgXxtkv42toX5f1anN77A242lqx4Ix3xuM+d5ojmPuMmtdYw8hbPfytbe26us1Olw39mmMqfd93xXTCP7yl78YSWbPnj0h62fPnm0effTRW/rPnz/fSGJhYWFhYWFpAUtJScm9ihx31Cxukc/Ly1Nubq7zuKqqSuXl5erQoYMiIiLuer8VFRVKSUlRSUmJ3G53fQy1ybKpVsmuem2qVbKrXptqleyq16Zapf+rt7i4WBEREUpOTm7sIUlqpLfDOnbsqKioKJWWloasLy0tldfrvaW/y+WSy+UKWRcXF1dv43G73Vb8TyjZVatkV7021SrZVa9NtUp21WtTrZLk8XiaVL2NcmF0dHS0Bg4cqO3btzvrqqqqtH37dvl8vsYYEgAAsEyjvR2Wm5ur7OxsDRo0SI8++qiWL1+uy5cva/LkyY01JAAAYJFGC0Hf//73de7cOc2bN09+v1/9+/fXli1blJiYeM/G4HK5NH/+/FveamuJbKpVsqtem2qV7KrXplolu+q1qVap6dYbYUxTuU8NAADg3uG7wwAAgJUIQQAAwEqEIAAAYCVCEAAAsFKLCEHdunVTREREyLJ48eKQPgcPHtTjjz+umJgYpaSkaMmSJbfsZ926derdu7diYmLUt29fffTRRyHtxhjNmzdPSUlJatOmjdLT0/XVV1+F9CkvL9eECRPkdrsVFxenKVOm6NKlS/VeczAYVP/+/RUREaEDBw44619++eVbfhcRERFq27at02f16tW3tMfExDTZWu9U7+nTp6utd+/evSHbt4Rju2vXLo0bN05JSUlq27at+vfvrzVr1oRs25KOrdRy5u0TTzyhLl26KCYmRklJSZo4caLOnDnjtLe0eVtTvS1p3tZUa0uatzXVKjXDOdtIX9dRr7p27WoWLlxozp496yyXLl1y2gOBgElMTDQTJkwwhw8fNu+++65p06aNeeONN5w+n376qYmKijJLliwxR48eNXPnzjWtW7c2hw4dcvosXrzYeDwe88EHH5g//elP5oknnjDdu3c3//M//+P0GTlypHn44YfN3r17zR/+8Adz//33m2eeeabea/7xj39sRo0aZSSZL774wll/8eLFkN/D2bNnTWpqqsnOznb6vPXWW8btdof08fv9IftvSrXeqd5Tp04ZSebjjz8Oqefq1atOn5ZybH/2s5+ZuXPnmk8//dScOHHCLF++3ERGRpqNGzc6fVrSsW1J8/YXv/iFKSwsNKdPnzaffvqp8fl8xufzOe0tbd7WVG9Lmrc11dqS5m1NtTbHOdtiQtCyZctu2/7aa6+Zv/mbvzHBYNBZN2fOHNOrVy/n8d/93d+ZzMzMkO3S0tLMj370I2OMMVVVVcbr9ZqlS5c67RcuXDAul8u8++67xhhjjh49aiSZ/fv3O302b95sIiIizF/+8pc61Xizjz76yPTu3dscOXLklj8c33TgwAEjyezevdtZ99ZbbxmPx3PbbZpSrcbcud4bL6Z3+h201GNrjDGjR482kydPdh63pGPb0ubtzTZs2GAiIiJC/ujfrCXM25t9s96WNm9vVtOxNab5z9sbvllrc5yzLeLtMElavHixOnTooAEDBmjp0qWqrKx02goLCzV06FBFR0c76zIyMnT8+HH993//t9MnPT09ZJ8ZGRkqLCyUJJ06dUp+vz+kj8fjUVpamtOnsLBQcXFxGjRokNMnPT1dkZGR2rdvX73UWVpaqqlTp+o//uM/FBsbW2P/X//61+rZs6cef/zxkPWXLl1S165dlZKSonHjxunIkSNOW1OpVap9vU888YQSEhI0ZMgQ/ed//mdIW0s9tpIUCAQUHx8fsq6lHNuWNG9vVl5erjVr1uixxx5T69atq+3T3Oftze5Ub0uYtzerzbGVmve8vaG6WpvjnG0RIejHP/6x1q5dq507d+pHP/qRXnnlFb3wwgtOu9/vv+WTqG889vv9d+xzc/vN292uT0JCQkh7q1atFB8f7/SpC2OMnn32WU2fPj3k4N/OlStXtGbNGk2ZMiVkfa9evfTmm29qw4YNeuedd1RVVaXHHntMX3/9tVPHjdpudi9rlWpXb7t27fTzn/9c69at04cffqghQ4boySefDHlBbYnHVpLee+897d+/P+SrZlrSsW0p8/aGOXPmqG3bturQoYOKi4u1YcOGavs193l7w53qbSnztja1flNznrfSnWttjnO2yYagF198sdoL525evvzyS0n/+z1kw4YNU79+/TR9+nT9/Oc/14oVKxQMBhu5itqpba0rVqzQxYsXlZeXV6v9rl+/XhcvXlR2dnbIep/Pp0mTJql///769re/rffff1+dOnXSG2+80RDl3aI+6+3YsaNyc3OVlpamRx55RIsXL9YPf/hDLV269J7UUpOGOrY7d+7U5MmT9W//9m968MEHnfUt6dg2deG8RknS7Nmz9cUXX+j3v/+9oqKiNGnSJJlqPrC/uc/bG+5Ub0uZtzfU9tg2xXnbULU2F4323WE1+clPfqJnn332jn3uu+++atenpaWpsrJSp0+fVq9eveT1elVaWhrS58Zjr9fr/Le6Pje331iXlJQU0qd///5On7KyspB9VFZWqry83Nm+LrXu2LFDhYWFt3z3yqBBgzRhwgS9/fbbIet//etfa8yYMTV+H1vr1q01YMAAnThxosFrlRqu3hvS0tK0bds253FLO7YFBQUaO3asli1bpkmTJt1x38352LaUeXtDx44d1bFjR/Xs2VN9+vRRSkqK9u7dK5/PF7JNc5+34dZ7Q3Oct+HU2lTnbX3W2tTnbLXCuoKomXjnnXdMZGSkKS8vN8b838VaN1+olpeXd8vFWmPGjAnZj8/nu+VirVdffdVpDwQC1V6s9fnnnzt9tm7dWm8Xpv35z382hw4dcpatW7caSeZ3v/udKSkpCen7X//1XyYiIiLkDoTbqaysNL169TKzZs1qMrUaE169N/v7v/97M2DAAOdxSzq2O3fuNG3btjX/8i//Uqv9Nudj21LmbXX+/Oc/G0lm586dIetbwrytzu3qvVlznLfVqa7WljJvv+mbtTbHOdvsQ9CePXvMsmXLzIEDB8zJkyfNO++8Yzp16mQmTZrk9Llw4YJJTEw0EydONIcPHzZr1641sbGxt9y216pVK/Pqq6+aY8eOmfnz51d7215cXJzZsGGDOXjwoBk3bly1t+0NGDDA7Nu3z3zyySfmgQceaLBbi+90h8XcuXNNcnKyqaysvKVtwYIFZuvWrebkyZOmqKjIjB8/3sTExJgjR4402VqNqb7e1atXm/z8fHPs2DFz7Ngx87Of/cxERkaaN9980+nTUo7tjh07TGxsrMnLywu5lfb8+fNOn5Z0bFvKvN27d69ZsWKF+eKLL8zp06fN9u3bzWOPPWZ69Ohhrly5EtK3Jczb2tTbUuZtbWptKfO2NrU2xznb7ENQUVGRSUtLMx6Px8TExJg+ffqYV1555ZYXlz/96U9myJAhxuVymb/92781ixcvvmVf7733nunZs6eJjo42Dz74oPnwww9D2quqqsxLL71kEhMTjcvlMsOHDzfHjx8P6XP+/HnzzDPPmHbt2hm3220mT55sLl68WP+Fm9uHoOvXr5vOnTubn/70p9Vu9/zzz5suXbqY6Ohok5iYaEaPHm3++Mc/hvRparUac/sQ1KdPHxMbG2vcbrd59NFHzbp1627ZtiUc2+zsbCPpluXb3/6206clHVtjWsa8PXjwoPnOd75j4uPjjcvlMt26dTPTp083X3/9dUi/ljJva1NvS5m3tam1pczb2v5/3NzmbIQxzfiKJgAAgLvUZO8OAwAAaEiEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABY6f8BuZoJxjSNTGkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Upper and lower bound\n", + "\n", + "query = \"energy: [-50000 TO -30000]\"\n", + "data = abcd.property('energy', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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VwMAAFBLYYWggoIC5eTkaO/evdq2bZuuXbumESNG6PLly06fWbNmaePGjVq3bp0KCgp05swZPfXUU0779evXlZmZqatXr2rPnj16++23tXr1as2bN6/+qgIAAKhBhDHG3O3G586dU0JCggoKCjR06FAFAgF16tRJ+fn5evrppyVJX375pfr06aPCwkINHjxYmzdv1pgxY3TmzBklJiZKklatWqU5c+bo3Llzio6OrvF5Kyoq5PF4FAgE5Ha773b4aEDdXvywxj6nF2feg5EAAL6psV6jm9rf7zpdExQIBCRJ8fHxkqSioiJdu3ZN6enpTp/evXurS5cuKiwslCQVFhaqb9++TgCSpIyMDFVUVOjIkSPVPk8wGFRFRUXIAgAAUBd3HYKqqqr0/PPP61vf+pYeeughSZLf71d0dLTi4uJC+iYmJsrv9zt9bg5AN9pvtFVn0aJF8ng8zpKSknK3wwYAAJBUhxCUk5Ojw4cPa+3atfU5nmrl5eUpEAg4S0lJSYM/JwAAaNla3c1GM2fO1KZNm7R792517tzZWe/1enX16lVduHAh5GxQaWmpvF6v0+ezzz4L2d+Nu8du9Pkml8sll8t1N0MFAACoVlhngowxmjlzptavX68dO3aoe/fuIe0DBw5U69attX37dmfd8ePHVVxcLJ/PJ0ny+Xw6dOiQysrKnD7btm2T2+1WampqXWoBAACotbDOBOXk5Cg/P18bNmxQ+/btnWt4PB6P2rRpI4/HoylTpig3N1fx8fFyu9167rnn5PP5NHjwYEnSiBEjlJqaqokTJ2rJkiXy+/2aO3eucnJyONsDAADumbBC0Ouvvy5JGjZsWMj6t956S88++6wkadmyZYqMjFRWVpaCwaAyMjL02muvOX2joqK0adMmzZgxQz6fT23btlV2drYWLlxYt0oAAADCEFYIqs1HCsXExGjlypVauXLlbft07dpVH330UThPDQAAUK/47jAAAGAlQhAAALASIQgAAFiJEAQAAKxECAIAAFYiBAEAACsRggAAgJUIQQAAwEqEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABYiRAEAACsRAgCAABWIgQBAAArEYIAAICVCEEAAMBKhCAAAGAlQhAAALASIQgAAFiJEAQAAKxE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02f16tW3tMfExDTZWu9U7+nTp6utd+/evSHbt4Rju2vXLo0bN05JSUlq27at+vfvrzVr1oRs25KOrdRy5u0TTzyhLl26KCYmRklJSZo4caLOnDnjtLe0eVtTvS1p3tZUa0uatzXVKjXDOdtIX9dRr7p27WoWLlxozp496yyXLl1y2gOBgElMTDQTJkwwhw8fNu+++65p06aNeeONN5w+n376qYmKijJLliwxR48eNXPnzjWtW7c2hw4dcvosXrzYeDwe88EHH5g//elP5oknnjDdu3c3//M//+P0GTlypHn44YfN3r17zR/+8Adz//33m2eeeabea/7xj39sRo0aZSSZL774wll/8eLFkN/D2bNnTWpqqsnOznb6vPXWW8btdof08fv9IftvSrXeqd5Tp04ZSebjjz8Oqefq1atOn5ZybH/2s5+ZuXPnmk8//dScOHHCLF++3ERGRpqNGzc6fVrSsW1J8/YXv/iFKSwsNKdPnzaffvqp8fl8xufzOe0tbd7WVG9Lmrc11dqS5m1NtTbHOdtiQtCyZctu2/7aa6+Zv/mbvzHBYNBZN2fOHNOrVy/n8d/93d+ZzMzMkO3S0tLMj370I2OMMVVVVcbr9ZqlS5c67RcuXDAul8u8++67xhhjjh49aiSZ/fv3O302b95sIiIizF/+8pc61Xizjz76yPTu3dscOXLklj8c33TgwAEjyezevdtZ99ZbbxmPx3PbbZpSrcbcud4bL6Z3+h201GNrjDGjR482kydPdh63pGPb0ubtzTZs2GAiIiJC/ujfrCXM25t9s96WNm9vVtOxNab5z9sbvllrc5yzLeLtMElavHixOnTooAEDBmjp0qWqrKx02goLCzV06FBFR0c76zIyMnT8+HH993//t9MnPT09ZJ8ZGRkqLCyUJJ06dUp+vz+kj8fjUVpamtOnsLBQcXFxGjRokNMnPT1dkZGR2rdvX73UWVpaqqlTp+o//uM/FBsbW2P/X//61+rZs6cef/zxkPWXLl1S165dlZKSonHjxunIkSNOW1OpVap9vU888YQSEhI0ZMgQ/ed//mdIW0s9tpIUCAQUHx8fsq6lHNuWNG9vVl5erjVr1uixxx5T69atq+3T3Oftze5Ub0uYtzerzbGVmve8vaG6WpvjnG0RIejHP/6x1q5dq507d+pHP/qRXnnlFb3wwgtOu9/vv+WTqG889vv9d+xzc/vN292uT0JCQkh7q1atFB8f7/SpC2OMnn32WU2fPj3k4N/OlStXtGbNGk2ZMiVkfa9evfTmm29qw4YNeuedd1RVVaXHHntMX3/9tVPHjdpudi9rlWpXb7t27fTzn/9c69at04cffqghQ4boySefDHlBbYnHVpLee+897d+/P+SrZlrSsW0p8/aGOXPmqG3bturQoYOKi4u1YcOGavs193l7w53qbSnztja1flNznrfSnWttjnO2yYagF198sdoL525evvzyS0n/+z1kw4YNU79+/TR9+nT9/Oc/14oVKxQMBhu5itqpba0rVqzQxYsXlZeXV6v9rl+/XhcvXlR2dnbIep/Pp0mTJql///769re/rffff1+dOnXSG2+80RDl3aI+6+3YsaNyc3OVlpamRx55RIsXL9YPf/hDLV269J7UUpOGOrY7d+7U5MmT9W//9m968MEHnfUt6dg2deG8RknS7Nmz9cUXX+j3v/+9oqKiNGnSJJlqPrC/uc/bG+5Ub0uZtzfU9tg2xXnbULU2F4323WE1+clPfqJnn332jn3uu+++atenpaWpsrJSp0+fVq9eveT1elVaWhrS58Zjr9fr/Le6Pje331iXlJQU0qd///5On7KyspB9VFZWqry83Nm+LrXu2LFDhYWFt3z3yqBBgzRhwgS9/fbbIet//etfa8yYMTV+H1vr1q01YMAAnThxosFrlRqu3hvS0tK0bds253FLO7YFBQUaO3asli1bpkmTJt1x38352LaUeXtDx44d1bFjR/Xs2VN9+vRRSkqK9u7dK5/PF7JNc5+34dZ7Q3Oct+HU2lTnbX3W2tTnbLXCuoKomXjnnXdMZGSkKS8vN8b838VaN1+olpeXd8vFWmPGjAnZj8/nu+VirVdffdVpDwQC1V6s9fnnnzt9tm7dWm8Xpv35z382hw4dcpatW7caSeZ3v/udKSkpCen7X//1XyYiIiLkDoTbqaysNL169TKzZs1qMrUaE169N/v7v/97M2DAAOdxSzq2O3fuNG3btjX/8i//Uqv9Nudj21LmbXX+/Oc/G0lm586dIetbwrytzu3qvVlznLfVqa7WljJvv+mbtTbHOdvsQ9CePXvMsmXLzIEDB8zJkyfNO++8Yzp16mQmTZrk9Llw4YJJTEw0EydONIcPHzZr1641sbGxt9y216pVK/Pqq6+aY8eOmfnz51d7215cXJzZsGGDOXjwoBk3bly1t+0NGDDA7Nu3z3zyySfmgQceaLBbi+90h8XcuXNNcnKyqaysvKVtwYIFZuvWrebkyZOmqKjIjB8/3sTExJgjR4402VqNqb7e1atXm/z8fHPs2DFz7Ngx87Of/cxERkaaN9980+nTUo7tjh07TGxsrMnLywu5lfb8+fNOn5Z0bFvKvN27d69ZsWKF+eKLL8zp06fN9u3bzWOPPWZ69Ohhrly5EtK3Jczb2tTbUuZtbWptKfO2NrU2xznb7ENQUVGRSUtLMx6Px8TExJg+ffqYV1555ZYXlz/96U9myJAhxuVymb/92781ixcvvmVf7733nunZs6eJjo42Dz74oPnwww9D2quqqsxLL71kEhMTjcvlMsOHDzfHjx8P6XP+/HnzzDPPmHbt2hm3220mT55sLl68WP+Fm9uHoOvXr5vOnTubn/70p9Vu9/zzz5suXbqY6Ohok5iYaEaPHm3++Mc/hvRparUac/sQ1KdPHxMbG2vcbrd59NFHzbp1627ZtiUc2+zsbCPpluXb3/6206clHVtjWsa8PXjwoPnOd75j4uPjjcvlMt26dTPTp083X3/9dUi/ljJva1NvS5m3tam1pczb2v5/3NzmbIQxzfiKJgAAgLvUZO8OAwAAaEiEIAAAYCVCEAAAsBIhCAAAWIkQBAAArEQIAgAAViIEAQAAKxGCAACAlQhBAADASoQgAABgJUIQAACwEiEIAABY6f8BuZoJxjSNTGkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# No upper bound\n", + "\n", + "query = \"energy: [-50000 TO *]\"\n", + "data = abcd.property('energy', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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oAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFOeAWg8AALvq6LkP1XoE9hBnUACA4ggUAKA4vQ6Uxx9/POecc05GjhyZurq6PPDAAz2OX3TRRamrq+uxTZ06tceaTZs2ZebMmWloaMiwYcMya9asvPXWWx/pjgAA+45eB8qWLVty0kkn5Y477njfNVOnTs1rr71W3f7hH/6hx/GZM2dm9erVWbZsWZYsWZLHH388l1xySe+nBwD2Sb1+k+y0adMybdq0D1xTX1+flpaW9zz285//PEuXLs0zzzyTU045JUly++2356yzzsrNN9+ckSNH9nYkAGAf0y/vQfne976X5ubmfPKTn8xll12WN954o3ps5cqVGTZsWDVOkmTSpEkZMGBAnn766fe8vu7u7nR1dfXYAIB9V58HytSpU3PPPfdk+fLl+fM///OsWLEi06ZNy44dO5IkHR0daW5u7vE3BxxwQJqamtLR0fGe17lgwYI0NjZWt1GjRvX12ABAQfr8e1DOP//86r9POOGEnHjiiTn22GPzve99L2eeeeZuXee8efMyZ86c6uWuri6RAgD7sH7/mPExxxyTww8/PC+++GKSpKWlJa+//nqPNdu3b8+mTZve930r9fX1aWho6LEBAPuufg+UX/7yl3njjTcyYsSIJElbW1s2b96cVatWVdc8+uij2blzZ1pbW/t7HABgL9Drl3jeeuut6tmQJFm7dm2effbZNDU1pampKV/96lczY8aMtLS05KWXXso111yTj33sY5kyZUqS5LjjjsvUqVNz8cUXZ+HChdm2bVtmz56d888/3yd4AIAku3EG5Uc/+lHGjx+f8ePHJ0nmzJmT8ePHZ/78+Rk4cGCee+65/M7v/E4+8YlPZNasWZkwYUKeeOKJ1NfXV6/j3nvvzdixY3PmmWfmrLPOyumnn56//uu/7rt7BQDs1Xp9BmXixImpVCrve/y73/3uh15HU1NTFi9e3NubBgD2E36LBwAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AA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FEegAADFESgAQHEECgBQHIFSqBtvvDG/9Vu/lSFDhrzvl9KtW7cu06dPz5AhQ9Lc3Jyrr74627dv37ODkiT5z//8z3zuc5/L4YcfnoaGhpx++ul57LHHaj0W/89DDz2U1tbWDB48OIceemjOPffcWo/Er+nu7s7JJ5+curq6PPvss7UehyQvv/xyZs2alTFjxmTw4ME59thjc/3112fr1q39ftsCpVBbt27Neeedl8suu+w9j+/YsSPTp0/P1q1b8+STT+buu+/OokWLMn/+/D08KUly9tlnZ/v27Xn00UezatWqnHTSSTn77LPT0dFR69FI8s///M+54IIL8uUvfzn/8R//kR/84Af54he/WOux+DXXXHPNLn0FOnvOCy+8kJ07d+Zb3/pWVq9enVtuuSULFy7MV77ylf6/8b75+T/6y1133VVpbGx81/6HH364MmDAgEpHR0d135133llpaGiodHd378EJ2bhxYyVJ5fHHH6/u6+rqqiSpLFu2rIaTUalUKtu2bav8xm/8RuVv//Zvaz0KH+Dhhx+ujB07trJ69epKkspPfvKTWo/E+7jpppsqY8aM6ffbcQZlL7Vy5cqccMIJPb7YbsqUKenq6srq1atrONn+57DDDssnP/nJ3HPPPdmyZUu2b9+eb33rW2lubs6ECRNqPd5+78c//nFeffXVDBgwIOPHj8+IESMybdq0PP/887Uejf+zYcOGXHzxxfn7v//7DBkypNbj8CE6OzvT1NTU77cjUPZSHR0d7/rW3Xcue1lhz6qrq8sjjzySn/zkJxk6dGgOOuig/OVf/mWWLl2aQw89tNbj7ff+67/+K0lyww035LrrrsuSJUty6KGHZuLEidm0aVONp6NSqeSiiy7KpZdemlNOOaXW4/AhXnzxxdx+++35wz/8w36/LYGyB82dOzd1dXUfuL3wwgu1HpP/s6uPV6VSSXt7e5qbm/PEE0/khz/8Yc4999ycc845ee2112p9N/ZZu/r47Ny5M0nyx3/8x5kxY0YmTJiQu+66K3V1dbn//vtrfC/2Xbv6+Nx+++158803M2/evFqPvF/ZneejV199NVOnTs15552Xiy++uN9n9FX3e9DGjRvzxhtvfOCaY445JoMGDapeXrRoUa644ops3ry5x7r58+fnX//1X3u8033t2rU55phj8uMf/zjjx4/vy9H3S7v6eD3xxBOZPHly/vu//7vHz5J//OMfz6xZszJ37tz+HnW/tKuPzw9+8IN89rOfzRNPPJHTTz+9eqy1tTWTJk3KjTfe2N+j7pd29fH5vd/7vTz44IOpq6ur7t+xY0cGDhyYmTNn5u677+7vUfdLvX0+Wr9+fSZOnJjTTjstixYtyoAB/X9+o2Y/Frg/OuKII3LEEUf0yXW1tbXlxhtvzOuvv57m5uYkybJly9LQ0JBx48b1yW3s73b18frVr36VJO/6D3bAgAHV//dO39vVx2fChAmpr6/PmjVrqoGybdu2vPzyyxk9enR/j7nf2tXH57bbbsvXv/716uX169dnypQpue+++9La2tqfI+7XevN89Oqrr+Yzn/lM9ezjnoiTRKAUa926ddm0aVPWrVuXHTt2VM+UfOxjH8shhxySyZMnZ9y4cbngggty0003paOjI9ddd13a29v9Ouge1tbWlkMPPTQXXnhh5s+fn8GDB+dv/uZvsnbt2kyfPr3W4+33Ghoacumll+b666/PqFGjMnr06PzFX/xFkuS8886r8XQcddRRPS4fcsghSZJjjz02Rx55ZC1G4v959dVXM3HixIwePTo333xzNm7cWD3W0tLSvzfe758TYrdceOGFlSTv2h577LHqmpdffrkybdq0yuDBgyuHH3545aqrrqps27atdkPvx5555pnK5MmTK01NTZWhQ4dWTjvttMrDDz9c67H4P1u3bq1cddVVlebm5srQoUMrkyZNqjz//PO1Hov3sHbtWh8zLshdd931ns9FeyIfvAcFACiOT/EAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAU538AcYWkS1fIrv0AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# All values of an array\n", + "\n", + "query = None\n", + "data = abcd.property('forces', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1356\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Range for an array (match if any values of array lie in range)\n", + "\n", + "query = \"forces: [-5 TO -3]\"\n", + "print(abcd.count(query))\n", + "data = abcd.property('forces', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Script queries" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "forces = [atoms.arrays[\"forces\"] for atoms in abcd.get_atoms()]" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(114, 3)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "forces[0].shape" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGzCAYAAAAFROyYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAllUlEQVR4nO3df3DU9Z3H8dcmMT8I2UCATcpBAJEaM4DYILAtZwFzRBocOaPWHsVAKVpcLBCPmrQ0iNUG0FGEQ0NtCxRlcKjajkGgaVDolIAYyg2g5KxHmhxxEywli7kjv/Z7f3TY6ZooSdjN95PwfMzsjPv9fve77y+Q5Ol3v7txWJZlCQAAwCARdg8AAADwWQQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAj7d27VxMmTFBsbKwcDocuXLhg90gAehCBAlyDtm7dKofDoffee69Hnu/999/X448/rqqqqk5t/9e//lX33Xef4uLitGnTJm3fvl3x8fHhHRKAUaLsHgBA3/f+++9r9erVmjZtmkaOHHnF7Y8ePaqLFy/qJz/5iTIzM8M/IADjcAYFgHHq6+slSQMGDAjZPhsbG0O2LwDhR6AA6FBzc7MKCwuVkZGhxMRExcfH65//+Z/19ttvt9t2586dysjIUEJCgpxOp8aNG6fnn39e0t9fTrr33nslSdOnT5fD4ZDD4dA777zT4fNOmzZNubm5kqRbb71VDodD8+fPD6zftWuXMjIyFBcXp8GDB+vb3/62zp49G7SP+fPnq3///vroo4/0jW98QwkJCZo7d64kye/36/nnn9e4ceMUGxurIUOG6I477mj3ctfLL78ceJ6kpCTdf//9qqmp6dafJYCuI1AAdMjn8+nnP/+5pk2bprVr1+rxxx/XuXPnlJWVpePHjwe2Ky0t1be+9S0NHDhQa9eu1Zo1azRt2jT98Y9/lCTddttt+v73vy9J+uEPf6jt27dr+/btuummmzp83h/96Ed68MEHJUlPPPGEtm/froceekjS32PnvvvuU2RkpIqKirRo0SK9/vrrmjp1aruLaFtbW5WVlSWXy6VnnnlGOTk5kqSFCxdq2bJlGj58uNauXav8/HzFxsbq8OHDgcc+9dRTeuCBBzRmzBg9++yzWrZsmcrKynTbbbdxsS7QUywA15wtW7ZYkqyjR49+7jatra1WU1NT0LK//e1vVnJysvWd73wnsGzp0qWW0+m0WltbP3dfu3btsiRZb7/9drfna25utlwulzV27Fjr//7v/wLLS0pKLElWYWFhYFlubq4lycrPzw/a7/79+y1J1ve///12z+n3+y3LsqyqqiorMjLSeuqpp4LWnzhxwoqKimq3HEB4cAYFQIciIyMVHR0t6e8vi5w/f16tra2aOHGijh07FthuwIABamxsVGlpaVjnee+991RfX6+HH35YsbGxgeXZ2dlKS0vT7t272z1m8eLFQfdfe+01ORwOrVq1qt22DodDkvT666/L7/frvvvu0yeffBK4paSkaMyYMR2+xAUg9AgUAJ9r27ZtGj9+vGJjYzVo0CANGTJEu3fvVkNDQ2Cbhx9+WF/+8pc1a9YsDRs2TN/5zne0d+/ekM/yl7/8RZJ04403tluXlpYWWH9ZVFSUhg0bFrTso48+0tChQ5WUlPS5z/Phhx/KsiyNGTNGQ4YMCbp98MEHgQt4AYQXbzMG0KGXX35Z8+fP15w5c7RixQq5XK7AtR8fffRRYDuXy6Xjx49r37592rNnj/bs2aMtW7bogQce0LZt22ybPyYmRhERXf9/ML/fL4fDoT179igyMrLd+v79+4diPABXQKAA6NCvf/1rXX/99Xr99dcDL39I6vDlkejoaN15552688475ff79fDDD2vz5s368Y9/rBtuuCHo8d01YsQISVJlZaVmzJgRtK6ysjKw/ouMHj1a+/bt0/nz5z/3LMro0aNlWZZGjRqlL3/5y1c9N4Du4SUeAB26fPbAsqzAsiNHjqi8vDxou7/+9a9B9yMiIjR+/HhJUlNTkyQFPgX2at4BM3HiRLlcLhUXFwf2K0l79uzRBx98oOzs7CvuIycnR5ZlafXq1e3WXT7Ou+++W5GRkVq9enXQsV/e5rPHCyA8OIMCXMN++ctfdni9yNKlSzV79my9/vrr+td//VdlZ2frzJkzKi4uVnp6uj799NPAtt/97nd1/vx5zZgxQ8OGDdNf/vIXbdy4URMmTAi8lXjChAmKjIzU2rVr1dDQoJiYGM2YMUMul6vTs1533XVau3atFixYoK9//ev61re+pbq6Oj3//PMaOXKkli9ffsV9TJ8+XfPmzdOGDRv04Ycf6o477pDf79cf/vAHTZ8+XUuWLNHo0aP15JNPqqCgQFVVVZozZ44SEhJ05swZvfHGG3rwwQf17//+752eG0A32fgOIgA2ufw23s+71dTUWH6/3/rpT39qjRgxwoqJibFuueUWq6SkxMrNzbVGjBgR2Nevf/1ra+bMmZbL5bKio6Ot1NRU66GHHrI+/vjjoOd86aWXrOuvv96KjIy84luOv+ht0K+++qp1yy23WDExMVZSUpI1d+5c63/+53+CtsnNzbXi4+M73Hdra6v19NNPW2lpaVZ0dLQ1ZMgQa9asWVZFRUXQdq+99po1depUKz4+3oqPj7fS0tIsj8djVVZWXuFPF0AoOCzrM+cwAQAAbMY1KAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwTq/8oDa/36/a2lolJCSE5CO0AQBA+FmWpYsXL2ro0KFX/F1ZvTJQamtrNXz4cLvHAAAA3VBTU9Put41/Vq8MlISEBEl/P0Cn02nzNAAAoDN8Pp+GDx8e+Dn+RXploFx+WcfpdBIoAAD0Mp25PIOLZAEAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYJwouwcAAPRuI/N3X3GbqjXZPTAJ+hLOoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADDOVQXKmjVr5HA4tGzZssCyS5cuyePxaNCgQerfv79ycnJUV1cX9Ljq6mplZ2erX79+crlcWrFihVpbW69mFAAA0Id0O1COHj2qzZs3a/z48UHLly9frjfffFO7du3SgQMHVFtbq7vvvjuwvq2tTdnZ2WpubtahQ4e0bds2bd26VYWFhd0/CgAA0Kd0K1A+/fRTzZ07Vy+99JIGDhwYWN7Q0KBf/OIXevbZZzVjxgxlZGRoy5YtOnTokA4fPixJ+t3vfqf3339fL7/8siZMmKBZs2bpJz/5iTZt2qTm5ubQHBUAAOjVuhUoHo9H2dnZyszMDFpeUVGhlpaWoOVpaWlKTU1VeXm5JKm8vFzjxo1TcnJyYJusrCz5fD6dOnWqw+dramqSz+cLugEAgL4rqqsP2Llzp44dO6ajR4+2W+f1ehUdHa0BAwYELU9OTpbX6w1s849xcnn95XUdKSoq0urVq7s6KgAA6KW6dAalpqZGS5cu1SuvvKLY2NhwzdROQUGBGhoaAreampoee24AANDzuhQoFRUVqq+v11e+8hVFRUUpKipKBw4c0IYNGxQVFaXk5GQ1NzfrwoULQY+rq6tTSkqKJCklJaXdu3ou37+8zWfFxMTI6XQG3QAAQN/VpUC5/fbbdeLECR0/fjxwmzhxoubOnRv47+uuu05lZWWBx1RWVqq6ulput1uS5Ha7deLECdXX1we2KS0tldPpVHp6eogOCwAA9GZdugYlISFBY8eODVoWHx+vQYMGBZYvXLhQeXl5SkpKktPp1COPPCK3260pU6ZIkmbOnKn09HTNmzdP69atk9fr1cqVK+XxeBQTExOiwwIAAL1Zly+SvZLnnntOERERysnJUVNTk7KysvTCCy8E1kdGRqqkpESLFy+W2+1WfHy8cnNz9cQTT4R6FAAA0Es5LMuy7B6iq3w+nxITE9XQ0MD1KABgs5H5u6+4TdWa7B6YBKbrys9vfhcPAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4UXYPAAAw18j83XaPgGsUZ1AAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGCcLgXKiy++qPHjx8vpdMrpdMrtdmvPnj2B9ZcuXZLH49GgQYPUv39/5eTkqK6uLmgf1dXVys7OVr9+/eRyubRixQq1traG5mgAAECf0KVAGTZsmNasWaOKigq99957mjFjhu666y6dOnVKkrR8+XK9+eab2rVrlw4cOKDa2lrdfffdgce3tbUpOztbzc3NOnTokLZt26atW7eqsLAwtEcFAAB6NYdlWdbV7CApKUlPP/207rnnHg0ZMkQ7duzQPffcI0k6ffq0brrpJpWXl2vKlCnas2ePZs+erdraWiUnJ0uSiouL9dhjj+ncuXOKjo7u1HP6fD4lJiaqoaFBTqfzasYHAHyBkfm7Q7KfqjXZIdkPereu/Pzu9jUobW1t2rlzpxobG+V2u1VRUaGWlhZlZmYGtklLS1NqaqrKy8slSeXl5Ro3blwgTiQpKytLPp8vcBamI01NTfL5fEE3AADQd0V19QEnTpyQ2+3WpUuX1L9/f73xxhtKT0/X8ePHFR0drQEDBgRtn5ycLK/XK0nyer1BcXJ5/eV1n6eoqEirV6/u6qgAAEN05kwMZ1nwj7p8BuXGG2/U8ePHdeTIES1evFi5ubl6//33wzFbQEFBgRoaGgK3mpqasD4fAACwV5fPoERHR+uGG26QJGVkZOjo0aN6/vnn9c1vflPNzc26cOFC0FmUuro6paSkSJJSUlL07rvvBu3v8rt8Lm/TkZiYGMXExHR1VAAA0Etd9eeg+P1+NTU1KSMjQ9ddd53KysoC6yorK1VdXS232y1JcrvdOnHihOrr6wPblJaWyul0Kj09/WpHAQAAfUSXzqAUFBRo1qxZSk1N1cWLF7Vjxw6988472rdvnxITE7Vw4ULl5eUpKSlJTqdTjzzyiNxut6ZMmSJJmjlzptLT0zVv3jytW7dOXq9XK1eulMfj4QwJAAAI6FKg1NfX64EHHtDHH3+sxMREjR8/Xvv27dO//Mu/SJKee+45RUREKCcnR01NTcrKytILL7wQeHxkZKRKSkq0ePFiud1uxcfHKzc3V0888URojwoAAPRqV/05KHbgc1AAoGeE6nNQOoN38fR9PfI5KAAAAOFCoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjBNl9wAAAHuMzN9t9wjA5+IMCgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAME6XAqWoqEi33nqrEhIS5HK5NGfOHFVWVgZtc+nSJXk8Hg0aNEj9+/dXTk6O6urqgraprq5Wdna2+vXrJ5fLpRUrVqi1tfXqjwYAAPQJXQqUAwcOyOPx6PDhwyotLVVLS4tmzpypxsbGwDbLly/Xm2++qV27dunAgQOqra3V3XffHVjf1tam7OxsNTc369ChQ9q2bZu2bt2qwsLC0B0VAADo1RyWZVndffC5c+fkcrl04MAB3XbbbWpoaNCQIUO0Y8cO3XPPPZKk06dP66abblJ5ebmmTJmiPXv2aPbs2aqtrVVycrIkqbi4WI899pjOnTun6OjoKz6vz+dTYmKiGhoa5HQ6uzs+AFzTRubvtnuEIFVrsu0eAWHWlZ/fV3UNSkNDgyQpKSlJklRRUaGWlhZlZmYGtklLS1NqaqrKy8slSeXl5Ro3blwgTiQpKytLPp9Pp06d6vB5mpqa5PP5gm4AAKDv6nag+P1+LVu2TF/72tc0duxYSZLX61V0dLQGDBgQtG1ycrK8Xm9gm3+Mk8vrL6/rSFFRkRITEwO34cOHd3dsAADQC3Q7UDwej06ePKmdO3eGcp4OFRQUqKGhIXCrqakJ+3MCAAD7RHXnQUuWLFFJSYkOHjyoYcOGBZanpKSoublZFy5cCDqLUldXp5SUlMA27777btD+Lr/L5/I2nxUTE6OYmJjujAoAAHqhLp1BsSxLS5Ys0RtvvKH9+/dr1KhRQeszMjJ03XXXqaysLLCssrJS1dXVcrvdkiS3260TJ06ovr4+sE1paamcTqfS09Ov5lgAAEAf0aUzKB6PRzt27NBvf/tbJSQkBK4ZSUxMVFxcnBITE7Vw4ULl5eUpKSlJTqdTjzzyiNxut6ZMmSJJmjlzptLT0zVv3jytW7dOXq9XK1eulMfj4SwJAACQ1MVAefHFFyVJ06ZNC1q+ZcsWzZ8/X5L03HPPKSIiQjk5OWpqalJWVpZeeOGFwLaRkZEqKSnR4sWL5Xa7FR8fr9zcXD3xxBNXdyQAAKDPuKrPQbELn4MCAFePz0FBT+uxz0EBAAAIBwIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMaJsnsAAAAkaWT+7ituU7UmuwcmgQk4gwIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAME6U3QMAAEJvZP5uu0cArgpnUAAAgHEIFAAAYBwCBQAAGIdrUAAAvUZnrq2pWpPdA5Mg3DiDAgAAjEOgAAAA43Q5UA4ePKg777xTQ4cOlcPh0G9+85ug9ZZlqbCwUF/60pcUFxenzMxMffjhh0HbnD9/XnPnzpXT6dSAAQO0cOFCffrpp1d1IAAAoO/ocqA0Njbq5ptv1qZNmzpcv27dOm3YsEHFxcU6cuSI4uPjlZWVpUuXLgW2mTt3rk6dOqXS0lKVlJTo4MGDevDBB7t/FAAAoE/p8kWys2bN0qxZszpcZ1mW1q9fr5UrV+quu+6SJP3qV79ScnKyfvOb3+j+++/XBx98oL179+ro0aOaOHGiJGnjxo36xje+oWeeeUZDhw69isMBAAB9QUivQTlz5oy8Xq8yMzMDyxITEzV58mSVl5dLksrLyzVgwIBAnEhSZmamIiIidOTIkQ7329TUJJ/PF3QDAAB9V0gDxev1SpKSk5ODlicnJwfWeb1euVyuoPVRUVFKSkoKbPNZRUVFSkxMDNyGDx8eyrEBAIBhesW7eAoKCtTQ0BC41dTU2D0SAAAIo5AGSkpKiiSprq4uaHldXV1gXUpKiurr64PWt7a26vz584FtPismJkZOpzPoBgAA+q6QBsqoUaOUkpKisrKywDKfz6cjR47I7XZLktxuty5cuKCKiorANvv375ff79fkyZNDOQ4AAOiluvwunk8//VR//vOfA/fPnDmj48ePKykpSampqVq2bJmefPJJjRkzRqNGjdKPf/xjDR06VHPmzJEk3XTTTbrjjju0aNEiFRcXq6WlRUuWLNH999/PO3gAAICkbgTKe++9p+nTpwfu5+XlSZJyc3O1detW/eAHP1BjY6MefPBBXbhwQVOnTtXevXsVGxsbeMwrr7yiJUuW6Pbbb1dERIRycnK0YcOGEBwOAADoCxyWZVl2D9FVPp9PiYmJamho4HoUAOhAZ36pXl/FLws0V1d+fveKd/EAAIBrC4ECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOFF2DwAA6JqR+bvtHgEIO86gAAAA43AGBQDQp3TmDFPVmuwemARXgzMoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4UXYPAABATxuZv/uK21Stye6BSfB5OIMCAACMQ6AAAADjECgAAMA4XIMCAAbpzLURwLWAMygAAMA4BAoAADAOgQIAAIxDoAAAAONwkSwAAB3gw9zsxRkUAABgHAIFAAAYh0ABAADGIVAAAIBxuEgWAHoInxILdB5nUAAAgHEIFAAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHN5mDABAN/H7esKHQAGAEOAzToDQsjVQNm3apKefflper1c333yzNm7cqEmTJtk5EoA+JFT/d0t8AD3PtkB59dVXlZeXp+LiYk2ePFnr169XVlaWKisr5XK57BoLQJgRDbjW8DJQ99gWKM8++6wWLVqkBQsWSJKKi4u1e/du/fKXv1R+fr5dYwEwAPEBwJZAaW5uVkVFhQoKCgLLIiIilJmZqfLy8nbbNzU1qampKXC/oaFBkuTz+cIy39hV+664zcnVWWF5bqArOvNvtTNC9e85VPP0pNTlu+weAejUz7POfn115uvZrp9zl4/TsqwrbmtLoHzyySdqa2tTcnJy0PLk5GSdPn263fZFRUVavXp1u+XDhw8P24xXkrjetqcGQo5/z4C9Qvk1GKp9hfP7wsWLF5WYmPiF2/SKd/EUFBQoLy8vcN/v9+v8+fMaNGiQHA6HjZN1js/n0/Dhw1VTUyOn02n3OD2KY+fYOfZrB8fOsV/p2C3L0sWLFzV06NAr7teWQBk8eLAiIyNVV1cXtLyurk4pKSntto+JiVFMTEzQsgEDBoRzxLBwOp3X3D/cyzh2jv1aw7Fz7Neazh77lc6cXGbLJ8lGR0crIyNDZWVlgWV+v19lZWVyu912jAQAAAxi20s8eXl5ys3N1cSJEzVp0iStX79ejY2NgXf1AACAa5dtgfLNb35T586dU2FhobxeryZMmKC9e/e2u3C2L4iJidGqVavavUx1LeDYOfZrDcfOsV9rwnXsDqsz7/UBAADoQfw2YwAAYBwCBQAAGIdAAQAAxiFQAACAcQgUAABgHAIlzJ566il99atfVb9+/T7302+rq6uVnZ2tfv36yeVyacWKFWptbe3ZQXvAf/3Xf+muu+7S4MGD5XQ6NXXqVL399tt2j9Vjdu/ercmTJysuLk4DBw7UnDlz7B6pRzU1NWnChAlyOBw6fvy43eOEXVVVlRYuXKhRo0YpLi5Oo0eP1qpVq9Tc3Gz3aGGxadMmjRw5UrGxsZo8ebLeffddu0cKu6KiIt16661KSEiQy+XSnDlzVFlZafdYtlizZo0cDoeWLVsWsn0SKGHW3Nyse++9V4sXL+5wfVtbm7Kzs9Xc3KxDhw5p27Zt2rp1qwoLC3t40vCbPXu2WltbtX//flVUVOjmm2/W7Nmz5fV67R4t7F577TXNmzdPCxYs0H/+53/qj3/8o/7t3/7N7rF61A9+8INO/f6NvuL06dPy+/3avHmzTp06peeee07FxcX64Q9/aPdoIffqq68qLy9Pq1at0rFjx3TzzTcrKytL9fX1do8WVgcOHJDH49Hhw4dVWlqqlpYWzZw5U42NjXaP1qOOHj2qzZs3a/z48aHdsYUesWXLFisxMbHd8rfeesuKiIiwvF5vYNmLL75oOZ1Oq6mpqQcnDK9z585ZkqyDBw8Glvl8PkuSVVpaauNk4dfS0mL90z/9k/Xzn//c7lFs89Zbb1lpaWnWqVOnLEnWn/70J7tHssW6deusUaNG2T1GyE2aNMnyeDyB+21tbdbQoUOtoqIiG6fqefX19ZYk68CBA3aP0mMuXrxojRkzxiotLbW+/vWvW0uXLg3ZvjmDYrPy8nKNGzcu6BN0s7Ky5PP5dOrUKRsnC61Bgwbpxhtv1K9+9Ss1NjaqtbVVmzdvlsvlUkZGht3jhdWxY8d09uxZRURE6JZbbtGXvvQlzZo1SydPnrR7tB5RV1enRYsWafv27erXr5/d49iqoaFBSUlJdo8RUs3NzaqoqFBmZmZgWUREhDIzM1VeXm7jZD2voaFBkvrc3/EX8Xg8ys7ODvr7DxUCxWZer7fdx/tfvt+XXvpwOBz6/e9/rz/96U9KSEhQbGysnn32We3du1cDBw60e7yw+u///m9J0uOPP66VK1eqpKREAwcO1LRp03T+/Hmbpwsvy7I0f/58fe9739PEiRPtHsdWf/7zn7Vx40Y99NBDdo8SUp988ona2to6/D7Wl76HXYnf79eyZcv0ta99TWPHjrV7nB6xc+dOHTt2TEVFRWHZP4HSDfn5+XI4HF94O336tN1j9ojO/llYliWPxyOXy6U//OEPevfddzVnzhzdeeed+vjjj+0+jG7p7LH7/X5J0o9+9CPl5OQoIyNDW7ZskcPh0K5du2w+iu7p7LFv3LhRFy9eVEFBgd0jh0x3vv7Pnj2rO+64Q/fee68WLVpk0+QIJ4/Ho5MnT2rnzp12j9IjampqtHTpUr3yyiuKjY0Ny3PY9ssCe7NHH31U8+fP/8Jtrr/++k7tKyUlpd3V7nV1dYF1puvsn8X+/ftVUlKiv/3tb3I6nZKkF154QaWlpdq2bZvy8/N7YNrQ6uyxXw6w9PT0wPKYmBhdf/31qq6uDueIYdOVv/fy8vJ2v0Rs4sSJmjt3rrZt2xbGKcOjq1//tbW1mj59ur761a/qZz/7WZin63mDBw9WZGRk4PvWZXV1db3ie1goLFmyRCUlJTp48KCGDRtm9zg9oqKiQvX19frKV74SWNbW1qaDBw/qP/7jP9TU1KTIyMireg4CpRuGDBmiIUOGhGRfbrdbTz31lOrr6+VyuSRJpaWlcjqdQT/QTNXZP4v//d//lfT316b/UUREROAMQ2/T2WPPyMhQTEyMKisrNXXqVElSS0uLqqqqNGLEiHCPGRadPfYNGzboySefDNyvra1VVlaWXn31VU2ePDmcI4ZNV77+z549q+nTpwfOmn32339fEB0drYyMDJWVlQXeOu/3+1VWVqYlS5bYO1yYWZalRx55RG+88YbeeecdjRo1yu6Resztt9+uEydOBC1bsGCB0tLS9Nhjj111nEgESthVV1fr/Pnzqq6uVltbW+DzH2644Qb1799fM2fOVHp6uubNm6d169bJ6/Vq5cqV8ng8ferXdrvdbg0cOFC5ubkqLCxUXFycXnrpJZ05c0bZ2dl2jxdWTqdT3/ve97Rq1SoNHz5cI0aM0NNPPy1Juvfee22eLrxSU1OD7vfv31+SNHr06D7/f5pnz57VtGnTNGLECD3zzDM6d+5cYF1fO7OQl5en3NxcTZw4UZMmTdL69evV2NioBQsW2D1aWHk8Hu3YsUO//e1vlZCQELjmJjExUXFxcTZPF14JCQntrrWJj4/XoEGDQncNTsjeD4QO5ebmWpLa3d5+++3ANlVVVdasWbOsuLg4a/Dgwdajjz5qtbS02Dd0mBw9etSaOXOmlZSUZCUkJFhTpkyx3nrrLbvH6hHNzc3Wo48+arlcLishIcHKzMy0Tp48afdYPe7MmTPXzNuMt2zZ0uHXfl/9trtx40YrNTXVio6OtiZNmmQdPnzY7pHC7vP+frds2WL3aLYI9duMHZZlWaFJHQAAgNDoey+IAgCAXo9AAQAAxiFQAACAcQgUAABgHAIFAAAYh0ABAADGIVAAAIBxCBQAAGAcAgUAABiHQAEAAMYhUAAAgHH+H+Y/bXR71PodAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "data = [np.mean(force) for force in forces]\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.title(\"Mean\")\n", + "plt.show()\n", + "\n", + "data = [force.min() for force in forces]\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.title(\"Min\")\n", + "plt.show()\n", + "\n", + "data = [force.max() for force in forces]\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.title(\"Max\")\n", + "plt.show()\n", + "\n", + "data = [force[0][0] for force in forces]\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.title(\"First force\")\n", + "plt.show()\n", + "\n", + "data = [force[-1][-1] for force in forces]\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.title(\"Last force\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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oAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFOeAWg8AALvq6LkP1XoE9hBnUACA4ggUAKA4vQ6Uxx9/POecc05GjhyZurq6PPDAAz2OX3TRRamrq+uxTZ06tceaTZs2ZebMmWloaMiwYcMya9asvPXWWx/pjgAA+45eB8qWLVty0kkn5Y477njfNVOnTs1rr71W3f7hH/6hx/GZM2dm9erVWbZsWZYsWZLHH388l1xySe+nBwD2Sb1+k+y0adMybdq0D1xTX1+flpaW9zz285//PEuXLs0zzzyTU045JUly++2356yzzsrNN9+ckSNH9nYkAGAf0y/vQfne976X5ubmfPKTn8xll12WN954o3ps5cqVGTZsWDVOkmTSpEkZMGBAnn766fe8vu7u7nR1dfXYAIB9V58HytSpU3PPPfdk+fLl+fM///OsWLEi06ZNy44dO5IkHR0daW5u7vE3BxxwQJqamtLR0fGe17lgwYI0NjZWt1GjRvX12ABAQfr8e1DOP//86r9POOGEnHjiiTn22GPzve99L2eeeeZuXee8efMyZ86c6uWuri6RAgD7sH7/mPExxxyTww8/PC+++GKSpKWlJa+//nqPNdu3b8+mTZve930r9fX1aWho6LEBAPuufg+UX/7yl3njjTcyYsSIJElbW1s2b96cVatWVdc8+uij2blzZ1pbW/t7HABgL9Drl3jeeuut6tmQJFm7dm2effbZNDU1pampKV/96lczY8aMtLS05KWXXso111yTj33sY5kyZUqS5LjjjsvUqVNz8cUXZ+HChdm2bVtmz56d888/3yd4AIAku3EG5Uc/+lHGjx+f8ePHJ0nmzJmT8ePHZ/78+Rk4cGCee+65/M7v/E4+8YlPZNasWZkwYUKeeOKJ1NfXV6/j3nvvzdixY3PmmWfmrLPOyumnn56//uu/7rt7BQDs1Xp9BmXixImpVCrve/y73/3uh15HU1NTFi9e3NubBgD2E36LBwAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AA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FEegAADFESgAQHEECgBQHIFSqBtvvDG/9Vu/lSFDhrzvl9KtW7cu06dPz5AhQ9Lc3Jyrr74627dv37ODkiT5z//8z3zuc5/L4YcfnoaGhpx++ul57LHHaj0W/89DDz2U1tbWDB48OIceemjOPffcWo/Er+nu7s7JJ5+curq6PPvss7UehyQvv/xyZs2alTFjxmTw4ME59thjc/3112fr1q39ftsCpVBbt27Neeedl8suu+w9j+/YsSPTp0/P1q1b8+STT+buu+/OokWLMn/+/D08KUly9tlnZ/v27Xn00UezatWqnHTSSTn77LPT0dFR69FI8s///M+54IIL8uUvfzn/8R//kR/84Af54he/WOux+DXXXHPNLn0FOnvOCy+8kJ07d+Zb3/pWVq9enVtuuSULFy7MV77ylf6/8b75+T/6y1133VVpbGx81/6HH364MmDAgEpHR0d135133llpaGiodHd378EJ2bhxYyVJ5fHHH6/u6+rqqiSpLFu2rIaTUalUKtu2bav8xm/8RuVv//Zvaz0KH+Dhhx+ujB07trJ69epKkspPfvKTWo/E+7jpppsqY8aM6ffbcQZlL7Vy5cqccMIJPb7YbsqUKenq6srq1atrONn+57DDDssnP/nJ3HPPPdmyZUu2b9+eb33rW2lubs6ECRNqPd5+78c//nFeffXVDBgwIOPHj8+IESMybdq0PP/887Uejf+zYcOGXHzxxfn7v//7DBkypNbj8CE6OzvT1NTU77cjUPZSHR0d7/rW3Xcue1lhz6qrq8sjjzySn/zkJxk6dGgOOuig/OVf/mWWLl2aQw89tNbj7ff+67/+K0lyww035LrrrsuSJUty6KGHZuLEidm0aVONp6NSqeSiiy7KpZdemlNOOaXW4/AhXnzxxdx+++35wz/8w36/LYGyB82dOzd1dXUfuL3wwgu1HpP/s6uPV6VSSXt7e5qbm/PEE0/khz/8Yc4999ycc845ee2112p9N/ZZu/r47Ny5M0nyx3/8x5kxY0YmTJiQu+66K3V1dbn//vtrfC/2Xbv6+Nx+++158803M2/evFqPvF/ZneejV199NVOnTs15552Xiy++uN9n9FX3e9DGjRvzxhtvfOCaY445JoMGDapeXrRoUa644ops3ry5x7r58+fnX//1X3u8033t2rU55phj8uMf/zjjx4/vy9H3S7v6eD3xxBOZPHly/vu//7vHz5J//OMfz6xZszJ37tz+HnW/tKuPzw9+8IN89rOfzRNPPJHTTz+9eqy1tTWTJk3KjTfe2N+j7pd29fH5vd/7vTz44IOpq6ur7t+xY0cGDhyYmTNn5u677+7vUfdLvX0+Wr9+fSZOnJjTTjstixYtyoAB/X9+o2Y/Frg/OuKII3LEEUf0yXW1tbXlxhtvzOuvv57m5uYkybJly9LQ0JBx48b1yW3s73b18frVr36VJO/6D3bAgAHV//dO39vVx2fChAmpr6/PmjVrqoGybdu2vPzyyxk9enR/j7nf2tXH57bbbsvXv/716uX169dnypQpue+++9La2tqfI+7XevN89Oqrr+Yzn/lM9ezjnoiTRKAUa926ddm0aVPWrVuXHTt2VM+UfOxjH8shhxySyZMnZ9y4cbngggty0003paOjI9ddd13a29v9Ouge1tbWlkMPPTQXXnhh5s+fn8GDB+dv/uZvsnbt2kyfPr3W4+33Ghoacumll+b666/PqFGjMnr06PzFX/xFkuS8886r8XQcddRRPS4fcsghSZJjjz02Rx55ZC1G4v959dVXM3HixIwePTo333xzNm7cWD3W0tLSvzfe758TYrdceOGFlSTv2h577LHqmpdffrkybdq0yuDBgyuHH3545aqrrqps27atdkPvx5555pnK5MmTK01NTZWhQ4dWTjvttMrDDz9c67H4P1u3bq1cddVVlebm5srQoUMrkyZNqjz//PO1Hov3sHbtWh8zLshdd931ns9FeyIfvAcFACiOT/EAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAU538AcYWkS1fIrv0AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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7fGbNm2y4uLirGeeecbatWuX9dBDD1ndu3e3du7cGa1DiKj2zs8jjzxivfnmm9aHH35o1dTUWLm5uVZiYqJVW1sbrUOImCNHjtjvK5KsZ5991tq2bZu1b98+y7Isq7i42Jo2bZo9/h//+IfVo0cPa+7cudauXbusxYsXW926dbPWr18frUOImIs+wCxfvtySdNbtlL1791qSrLfeestu+/LLL62f/vSnVp8+fawePXpYP/jBD0JCT1cyffr0s87P1+dDkrV8+XLLsizriy++sCZNmmT179/f6t69uzVo0CBrxowZ9ptRV9LeubGsry6lnj9/vuVyuayEhARr4sSJVl1dXccX3wE+++wz684777R69eplORwO69577w0Jd6f/bu3fv98aP368lZKSYiUkJFhXXHGFNXfuXCsQCETpCMJn0aJF1sCBA634+Hhr7Nix1rvvvmv33Xjjjdb06dNDxv/pT3+y/uVf/sWKj4+3rrzySuv111/v4Io7VnvmZ9asWfZYl8tlTZkyxdq6dWsUqo68t95666zvMafmY/r06daNN954xj6jRo2y4uPjrcsuuyzk/acribEsy+qo1R4AAIBw6LJXIQEAgK6LAAMAAIxDgAEAAMYhwAAAAOMQYAAAgHEIMAAAwDgEGAAAYBwCDAAAMA4BBgAAGIcAAwAAjEOAAQAAxiHAAAAA4/w/GoJibDbNJ0sAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualise forces\n", + "\n", + "query = None\n", + "data = abcd.property('forces', query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.show()\n", + "\n", + "data = abcd.count_property(\"forces\", query=query)\n", + "hist, bins, ax = plt.hist(data, bins=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "366\n" + ] + } + ], + "source": [ + "count = 0\n", + "for force in forces:\n", + " if np.sum(np.logical_and((force < 3), force > -3)) == force.size:\n", + " count +=1\n", + "print(count)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "366\n" + ] + } + ], + "source": [ + "# Check all forces for each structure are between -3 and 3\n", + "\n", + "script = \"\"\"\n", + "int count = 0;\n", + "for (int i=0; i 3 || doc.forces[i] < -3) {\n", + " count += 1; \n", + " }\n", + "}\n", + "return count == 0;\n", + "\"\"\"\n", + "\n", + "query = {\n", + " \"bool\": {\n", + " \"filter\": {\n", + " \"script\": {\n", + " \"script\": script\n", + " }\n", + " }\n", + " },\n", + "}\n", + "\n", + "\n", + "print(abcd.count(query=query, timeout=90))" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1321\n" + ] + } + ], + "source": [ + "# Check forces no greater than 3\n", + "\n", + "count = 0\n", + "for force in forces:\n", + " if force.max() > 3:\n", + " count +=1\n", + "print(count)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1321\n" + ] + } + ], + "source": [ + "# Check forces no greater than 3\n", + "\n", + "script = \"\"\"\n", + "double max = doc.forces[0];\n", + "for (int i=1; i 3.0;\n", + "\"\"\"\n", + "\n", + "\n", + "query = {\n", + " \"bool\": {\n", + " \"filter\": {\n", + " \"script\": {\n", + " \"script\": script\n", + " }\n", + " }\n", + " },\n", + "}\n", + "\n", + "\n", + "print(abcd.count(query=query, timeout=60))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1010\n" + ] + } + ], + "source": [ + "# Check average force less than 0\n", + "\n", + "count = 0\n", + "for force in forces:\n", + " if np.mean(force) < 0.0:\n", + " count +=1\n", + "print(count)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1005\n" + ] + } + ], + "source": [ + "# Check average force less than 0\n", + "\n", + "script = \"\"\"\n", + "double force = 0;\n", + "for (int i=0; i" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "(-30849.977784286697, -30845.753419331242)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = abcd.property('energy', query)\n", + "hist, bins, ax = plt.hist(data, bins=50)\n", + "plt.show()\n", + "min(data), max(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "968" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"bool\": {\n", + " \"must\": [\n", + " {\n", + " \"range\" : {\n", + " \"energy\" : {\n", + " \"gte\" : -30850,\n", + " \"lte\" : -30848,\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"match\": {\n", + " \"n_atoms\": 114\n", + " }\n", + " }\n", + " ]\n", + " }\n", + "}\n", + "abcd.count(query)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'count': 968, 'category': 'info', 'dtype': 'scalar(float)'}" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abcd.count_properties(query)['energy']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fetching the data" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "968" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = {\n", + " \"bool\": {\n", + " \"must\": [\n", + " {\n", + " \"range\" : {\n", + " \"energy\" : {\n", + " \"gte\" : -30850,\n", + " \"lte\" : -30848,\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"match\": {\n", + " \"n_atoms\": 114\n", + " }\n", + " }\n", + " ]\n", + " }\n", + "}\n", + "abcd.count(query)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "968" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "traj = list(abcd.get_atoms(query=query))\n", + "len(traj)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Atoms(symbols='C48H28O32Zr6', pbc=True, cell=[[14.759483662029265, 0.0, 0.0], [7.380258413807584, 12.781786651387147, 0.0], [7.380243655055182, 4.260782501715179, 12.050631347394049]], forces=...)" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "traj[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "242" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data = list(abcd.get_items(query=query))\n", + "len(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'_id': '2UDM2okBtksDlC5r8IGq',\n", + " 'n_atoms': 114,\n", + " 'cell': [[14.759483662029265, 0.0, 0.0],\n", + " [7.380258413807584, 12.781786651387147, 0.0],\n", + " [7.380243655055182, 4.260782501715179, 12.050631347394049]],\n", + " 'pbc': [True, True, True],\n", + " 'formula': 'C48H28O32Zr6',\n", + " 'numbers': [1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 1,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 6,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 8,\n", + " 40,\n", + " 40,\n", + " 40,\n", + " 40,\n", + " 40,\n", + " 40],\n", + " 'positions': [[17.166810638040264, 11.566799628342661, 2.3959431306453296],\n", + " [10.391931260040497, 9.232075241735581, 8.799170748954813],\n", + " [15.152442318761134, 3.2144705981189303, 0.6236271192356346],\n", + " [15.428455018627362, 13.198368239182761, 6.757442369774353],\n", + " [20.968952462595865, 8.354501228588285, 5.937790321351722],\n", + " [12.821718368988067, 11.860905590260213, 0.764468940894911],\n", + " [20.164574198879585, 13.449131931085539, 8.500504258460039],\n", + " [5.203325638335655, 4.037525599970674, 3.6535544413570706],\n", + " [6.476452578322519, 9.882112891744764, 0.7336632917566172],\n", + " [14.332783438660714, 4.5739237510789845, 5.763830060388294],\n", + " [12.20845295758527, 7.975607890442319, 2.7181563401019804],\n", + " [26.126453831046035, 15.25865575215541, 8.681035572143871],\n", + " [7.431639790543854, 7.68880010777489, 3.739705967641281],\n", + " [21.52510600020679, 15.432405681052952, 8.675468268048236],\n", + " [11.49107468172553, 9.60164215963523, 0.7009214784567679],\n", + " [18.70674083756121, 4.607625571215378, 5.677858158016438],\n", + " [17.34676875755316, 10.130528920703508, 4.483049170020872],\n", + " [2.9330861621787743, 3.3990818416373494, 0.720770788622487],\n", + " [22.67189915206641, 9.23882668038352, 8.661796350384211],\n", + " [15.54501705742674, 14.931708899088871, 4.905010140501105],\n", + " [20.180891240581246, 11.991517760259551, 0.36399634878062614],\n", + " [13.537900990107627, 8.71222318139275, 4.81955270950513],\n", + " [13.02082403030889, 3.7798366294145125, 3.8744821907763676],\n", + " [12.738608267554484, 13.15125952920471, 8.605595280531846],\n", + " [9.30512423974256, 4.003262597986021, 2.08391144947309],\n", + " [13.66172535110934, 6.786427797477926, 9.451058899918706],\n", + " [19.297496722626608, 6.66303528741421, 9.65403361924748],\n", + " [16.364750768476505, 11.479160632545504, 9.561987519221761],\n", + " [16.965060879285595, 12.039276627942046, 3.4048076978088133],\n", + " [9.872075532499599, 8.868306222192839, 9.697764141289875],\n", + " [23.093789800187132, 7.8655671087878325, 11.819108411864843],\n", + " [15.963054660441902, 12.957563889928995, 5.796919712452191],\n", + " [20.789096532494103, 7.4060484208004835, 5.418655862348822],\n", + " [20.271838371834924, 15.445983071791856, 11.893743962525676],\n", + " [20.235785459686173, 13.946169611391733, 9.533785527794883],\n", + " [5.468638782470736, 4.963675408702813, 4.207234520017469],\n", + " [13.30165782031905, 14.29182491089219, 11.848895929341783],\n", + " [13.860085269446175, 5.316906102226919, 5.113333629612867],\n", + " [12.690982408563865, 7.210540402496312, 3.452020777408105],\n", + " [26.695527891724396, 14.913992347710204, 9.505586002333807],\n", + " [6.71805210100351, 7.061748291218562, 4.294338782243351],\n", + " [20.991757417986378, 15.139143269943215, 9.631524849633491],\n", + " [19.51051107137078, 14.241804768445284, 11.863723702327984],\n", + " [19.467115888926717, 5.338714780974876, 5.342773829476735],\n", + " [17.04442989892783, 11.179573262577135, 4.5377135674632525],\n", + " [9.75214492091448, 7.909665520118783, 11.856922309351365],\n", + " [23.22825354947115, 8.805217731024735, 9.58442235219589],\n", + " [16.00517946832365, 13.88422938185283, 4.718010810191537],\n", + " [27.480789782170447, 15.480087960917762, 11.617172959535212],\n", + " [13.389347998136012, 7.596486021826197, 4.594226402069221],\n", + " [13.123855701056296, 4.872440299211145, 4.040302647886903],\n", + " [12.64753966896389, 13.717491998222464, 9.492966517597923],\n", + " [20.97530038245366, 8.335251317448973, 10.600423519640026],\n", + " [16.502636637988203, 10.85079294806207, 6.900496977690235],\n", + " [9.019607067373974, 1.3080109208445687, 2.1730394493971033],\n", + " [12.07299373071628, 8.404520741314311, 10.81235772773731],\n", + " [18.747600350981866, 12.258841111031488, 10.56065092033455],\n", + " [7.015172452108002, 5.4262894076561405, 2.347590240376888],\n", + " [18.78251160034123, 7.0857013283180255, 6.946566060828641],\n", + " [14.224469007695566, 4.466485050187827, 10.705760127563483],\n", + " [11.603451262170989, 5.349451807744306, 2.0915175410667244],\n", + " [18.543838401175798, 4.375079148751448, 10.585231910862703],\n", + " [14.160667831243819, 12.098710870007285, 10.795787760496086],\n", + " [14.45018544044642, 7.011509947840652, 6.832073401261807],\n", + " [16.477007379156557, 13.350335720646678, 3.479800617715256],\n", + " [10.582432902092235, 8.377782469927169, 10.803729958995012],\n", + " [22.44315777833504, 8.370354822180708, 10.648120637633818],\n", + " [16.567659589367246, 11.689973911016514, 5.702162918040675],\n", + " [19.752709342190204, 6.556676208576273, 5.912505297587319],\n", + " [20.937902942140084, 15.937436540790538, 10.817725992408736],\n", + " [19.488886710337034, 13.540165709815675, 10.668618978356523],\n", + " [6.371529009087222, 5.8630806488390075, 3.652784530849877],\n", + " [13.287113670442396, 13.309066380643525, 10.810269250499239],\n", + " [13.887372022428522, 6.66488149144997, 5.501186858702026],\n", + " [12.488596287754628, 5.853559681882998, 3.200679859004045],\n", + " [26.74856971012843, 15.90075025114669, 10.529236108850316],\n", + " [10.08315746894864, 1.501050200458931, 1.573941101482483],\n", + " [12.62721678223712, 8.955703669760464, 9.832552292079287],\n", + " [20.39919235012287, 7.4641321602901884, 11.35141521129136],\n", + " [15.34144808796582, 10.482235049392207, 7.284668181164747],\n", + " [19.016384691239136, 8.259448068479994, 7.407999661220768],\n", + " [13.965549620748263, 5.310837048175544, 11.629395661906845],\n", + " [18.964757008970206, 11.57454425853002, 9.498296919639932],\n", + " [6.724169659438005, 4.266531959190234, 1.8722045706136907],\n", + " [14.989309002358754, 11.848517668179902, 11.724244051601735],\n", + " [15.200359309885917, 6.086405481062033, 7.400205812261053],\n", + " [10.683892382055284, 6.184519761996729, 1.7395703404079805],\n", + " [17.662435367568307, 4.527759521878119, 9.64210249881064],\n", + " [7.857986041984485, 6.202923889200398, 1.7950802519056281],\n", + " [14.981497208999828, 4.615121609824532, 9.745747168438765],\n", + " [17.887354642477394, 12.0129465321045, 11.494403179921116],\n", + " [17.806453279094754, 6.309886859779258, 7.194991281595532],\n", + " [17.65884499015476, 10.488278242553303, 7.416379002429795],\n", + " [12.548166448986402, 7.693791677648976, 11.776895469518676],\n", + " [20.34393939120336, 9.206817729894354, 9.922408032378584],\n", + " [7.857463468905509, 1.74909233925955, 1.8274196418121598],\n", + " [18.857763430781485, 5.182306608404846, 11.489619301518633],\n", + " [14.163844810128705, 8.142657329873755, 7.360230728587728],\n", + " [11.792894920225645, 4.155462409412462, 1.7001921686281716],\n", + " [13.835964747759567, 11.264146337910587, 9.884038668157677],\n", + " [18.442882359243793, 7.121400257955315, 9.800715117804442],\n", + " [16.392129968717537, 10.582139510371382, 9.857218178507331],\n", + " [9.302433061412287, 3.9652007015997683, 1.0673996577713942],\n", + " [14.478398449342821, 7.162072897735833, 9.816748197723141],\n", + " [15.11113884153613, 9.160319431120458, 11.201390619831347],\n", + " [16.44457564782678, 6.551160139418165, 11.181147839950713],\n", + " [16.50639593935091, 8.350138210113135, 8.738223430491407],\n", + " [18.033642210935266, 9.122422759734661, 11.23846663261546],\n", + " [10.893906920444666, 2.90658841770915, -0.019051977695474183],\n", + " [14.757549238860731, 9.308555112561303, 9.214183379238824],\n", + " [18.225880348003468, 9.344784918068, 9.180851999025142],\n", + " [7.388359137923477, 3.030521667505481, 0.14692076074094484],\n", + " [9.220983449272417, 6.075785767588413, 0.00657883162539968],\n", + " [16.440728831136994, 6.2934328881825925, 9.187973671216682]],\n", + " 'forces': [[0.03505596579759337, 0.7596797943958487, 0.9211044616269563],\n", + " [0.16925367694563342, -0.01943702713953078, 0.38893903196958485],\n", + " [0.01574595116377608, 0.17132290092535438, -0.1999552221020049],\n", + " [0.6313750521363777, 0.05251478601615336, -0.8064430222079316],\n", + " [-0.09833287623511343, -0.138000887230052, -0.08874934559146055],\n", + " [0.19781246456634455, -0.9287673780647797, -1.0439826331463689],\n", + " [0.07987955323902354, 0.3227860853196942, 1.7840037712935266],\n", + " [0.3716884711227413, 0.4696845328184121, 0.370453313071228],\n", + " [-0.14715298673081575, -0.28619517081945, -0.2515490388965677],\n", + " [-0.019879256916508915, 0.1586797572898179, 0.03203954734206577],\n", + " [0.9120773177492224, -1.200046035662623, 1.3240873743396222],\n", + " [-0.5694124897336902, -0.12506360937075797, -1.3829512429794373],\n", + " [-0.0348132536409263, 0.07054439117941769, 0.5526864339711696],\n", + " [-0.41634750794948194, 0.12174380071939654, 1.3701661744387312],\n", + " [1.1617998448227365, 0.7015918847484289, -1.3710321220485349],\n", + " [0.19070902021846559, 0.8070636865577138, 0.18004202662121627],\n", + " [0.22735084256512936, 0.16909786808234928, -0.1733360748519467],\n", + " [-0.4446707825029187, -0.3087082660123333, -0.10186968600959667],\n", + " [1.0559542045857038, -0.7522446777152361, 1.3649098107407422],\n", + " [0.7762546692811232, -1.6189836731314526, -0.7746117342375823],\n", + " [0.028996903632322036, -0.4549212573567351, -0.15349589870642655],\n", + " [-0.30669869163043734, -1.474741689618629, -0.22373535702799768],\n", + " [-0.0288004713360364, 0.7124475973319003, -0.205686211479239],\n", + " [-0.009475030082091964, -0.9498180296696098, -0.7324728929189461],\n", + " [0.024624999488289372, -0.06313755663570486, -1.801891965772292],\n", + " [-0.06351139506345264, -0.159376012078223, -0.050473844173332186],\n", + " [-0.27379062557521333, 0.16125086064434194, -0.24263708044911655],\n", + " [0.16200265126520474, 1.5060489867253262, -0.5510645819751359],\n", + " [0.11499568285511277, -1.536591123473717, -0.46194088373525655],\n", + " [-0.3656006125998993, -0.17292778363924827, -1.3654302020596978],\n", + " [1.3289642431824835, 0.5107142001499065, -0.8944297785444403],\n", + " [-0.025697149587125892, 1.0899791579381328, 0.0022805686754628165],\n", + " [-0.7954947499036974, 0.11066234526139797, 1.0548332035231311],\n", + " [-1.8548078439117515, 0.38714131650410166, 2.989239836187592],\n", + " [0.4506861359111631, 1.1364487657471294, -0.6025277867192889],\n", + " [-1.2287668027946692, -0.09796263735206197, 0.2020090194615987],\n", + " [1.0444392611021323, -2.684139342402327, -1.0437898003947796],\n", + " [-0.07301933526848252, -0.6708481734970433, 0.9851917838418224],\n", + " [-0.8675355232354481, 0.9083975546282277, -1.1112478391074265],\n", + " [0.1290364782741685, 1.0164497157641128, 0.008965437397225301],\n", + " [0.2684093062541945, -0.321198686108611, -1.4872871313266933],\n", + " [-0.643392389215423, -0.898736890883939, -1.098486424717582],\n", + " [-0.03591728542135892, 0.6514399427150831, 0.9283400606872579],\n", + " [0.8447910287406576, -1.5317872739661245, -1.3569157361908561],\n", + " [0.17489879147082638, 0.8299521628403577, -1.920810638125738],\n", + " [0.3438521635446514, 0.034021868028403804, 0.7196574853586566],\n", + " [-0.49089716393457716, 1.32019318119886, -0.94368029174169],\n", + " [-0.8017672136473943, -0.4383576953262203, 0.7052387377464858],\n", + " [-0.5808395014825368, 3.6184937280564875, 2.299079591174048],\n", + " [-0.046264433761305436, 2.3374147421900013, 0.42705255387637825],\n", + " [-0.40195909935689456, 0.5967109224900137, -0.9082941962733758],\n", + " [0.545840099958743, 0.6951430433146117, 4.287385291900738],\n", + " [-0.6422225371891145, 0.3670378593750782, 0.6300133957998216],\n", + " [1.0342078124131395, -1.450620626387826, 0.9064692271123347],\n", + " [-1.6097744951362183, 0.26370058757071924, 1.6795418988818989],\n", + " [-1.71670514098886, -1.4817345765222634, 0.997617412133566],\n", + " [-0.36574665127043626, 0.3369549216858052, -0.5080099136416182],\n", + " [0.25302742332541905, -0.05521598720041631, 0.07509935788229247],\n", + " [0.4694140527455162, -0.5513201496485756, -0.6341384740218201],\n", + " [-0.9458055657745397, -0.43950337898099623, 2.0890692980731704],\n", + " [-0.10420990428288447, 1.1152443621616988, 0.8470916520222858],\n", + " [0.303680730512897, -1.6201972339147879, -2.1682659662612322],\n", + " [-1.573935371256837, -0.7716241121396255, 0.4766830761494265],\n", + " [0.8572325978731995, -0.38723439044553537, 0.7948401469896356],\n", + " [-0.2134458014031925, 0.03674209537749023, 0.28632115488382165],\n", + " [-2.3274044083894974, 0.6730330971277177, -0.3481623612081755],\n", + " [0.898834592811411, -1.0161931196493315, 0.970241846276607],\n", + " [-1.0154469854558483, 0.8937227251219462, 1.1335331345430992],\n", + " [-0.24175827732254024, 1.3876321937466838, 0.534561183763117],\n", + " [0.6898959355886991, -1.1971756558776325, -2.6977317373963534],\n", + " [0.3469128049778768, -0.8706239325849023, -0.7535286867304852],\n", + " [0.5164133078455445, -1.1737287359463862, -0.8774512404325054],\n", + " [0.07070174270471455, 2.0437350895055317, -2.2527981881306025],\n", + " [0.3288256070994712, -0.3856912142121501, -0.4543299035851946],\n", + " [0.46220673582211436, -1.5513682828935087, 0.2903063650833339],\n", + " [-0.38469619721394926, -2.2885375531998284, -0.3758814264732991],\n", + " [1.9752635218505312, 0.3340079230208498, -1.0452476159967947],\n", + " [0.8562020996487061, 0.4789574741768384, -0.4695076409076208],\n", + " [-0.07918329845061843, -0.20133898992740973, 0.4222739011816598],\n", + " [0.4557039012178522, 0.3706605440016026, 0.8159874720805957],\n", + " [-0.05972518826358055, -0.10986736010417913, 0.6435188875004655],\n", + " [0.0024615743516213087, 0.5677505285253259, 0.5866964749241426],\n", + " [-0.5868764521589592, -0.8929930459899325, 1.0299773189536057],\n", + " [-0.19385399384171936, 0.13704546522351088, 0.0855992297614977],\n", + " [0.5114222820137425, 0.9816344052405028, 0.021354041800663897],\n", + " [-1.066992979728017, 1.3317744591489784, -0.48617301863097473],\n", + " [0.22686850357582064, -0.6558709822362698, -0.6488395287823175],\n", + " [0.7797446849745541, 0.08798264257114148, 0.9232755012995165],\n", + " [0.23828317402856555, 0.1996127511351823, 0.16316684686413324],\n", + " [1.8341906803324939, 0.7465810512458905, -1.8839816394548647],\n", + " [0.25763432631605526, -0.3878936213456354, -0.15371958469827013],\n", + " [-0.09513905164812367, 0.4332483987401102, 1.2322542473847458],\n", + " [-1.7633197590270517, 0.7325978485419761, -0.6597173528546378],\n", + " [1.5710907025053915, 0.6496761658138797, -0.2693559865093303],\n", + " [0.13329885341529846, -0.16092278785630468, -0.8911017423623445],\n", + " [0.4560890525003599, -0.12238914766138208, 0.02436686071149516],\n", + " [0.04387896406897804, 0.4783136698968656, 0.7284398601970398],\n", + " [-0.19204033753543812, -0.5498325092476479, -0.4268478940493581],\n", + " [-0.04005727604281352, -0.4718303756780979, -0.6626314413966554],\n", + " [0.5963838781433183, -0.31271095971465634, -1.0048920919648563],\n", + " [0.2392545368759047, -0.07763909377589129, -0.3403374652590171],\n", + " [1.0458302280169298, -0.7758582051438556, -0.16412124042933254],\n", + " [-1.0646861857983383, 0.17288613176490497, 1.4279167553260372],\n", + " [0.310471014472195, 0.4872791072940909, 0.18266249516014715],\n", + " [-7.301933526848252e-05, -1.2856617204855898, 1.537077576228393],\n", + " [0.23109436904931635, 0.627365159344662, 1.1682219467816664],\n", + " [0.06169413921247506, -0.7133752514222126, 0.5373045510423942],\n", + " [-0.3402017110018982, -0.024654310066530946, -0.18435993759480393],\n", + " [-0.14411137146241382, 1.2003674235819386, 0.056440860838511554],\n", + " [-1.1119034704628301, 0.02676570014126608, -0.4762907257775261],\n", + " [0.753222725431297, 0.3270011521591009, -0.33560457820400924],\n", + " [-0.7786571082555904, 0.413619053069661, -1.222248027349609],\n", + " [-0.5719985054876705, -0.1103018765710937, 0.8759049788750947],\n", + " [-0.24311736255574165, -0.40464795924505575, -1.3254814265784451]],\n", + " 'energy': -30848.841105643754,\n", + " 'volume': 2273.382588904185,\n", + " 'elements': {'1': 28, '6': 48, '8': 32, '40': 6},\n", + " 'username': 'ubuntu',\n", + " 'uploaded': '2023-08-09T14:56:51.365526',\n", + " 'modified': '2023-08-09T14:56:51.365533',\n", + " 'hash_structure': '913be2ca3a0e3c584cc728f4c359c850',\n", + " 'hash': '96290c6b21554ece4011f07e63de82d3',\n", + " 'derived': {'arrays_keys': ['forces', 'positions', 'numbers'],\n", + " 'info_keys': ['pbc', 'n_atoms', 'cell', 'formula', 'energy', 'volume'],\n", + " 'results_keys': [],\n", + " 'derived_keys': ['elements',\n", + " 'username',\n", + " 'uploaded',\n", + " 'modified',\n", + " 'volume',\n", + " 'hash_structure',\n", + " 'hash']}}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[0]" + ] + } + ], + "metadata": { + "@webio": { + "lastCommId": null, + "lastKernelId": null + }, + "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.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/abcd_uploading.ipynb b/tutorials/abcd_uploading.ipynb index 80a774c8..c991db7f 100644 --- a/tutorials/abcd_uploading.ipynb +++ b/tutorials/abcd_uploading.ipynb @@ -203,7 +203,7 @@ "pycharm": {} }, "source": [ - "Uploading configurations on-by-one directly from an ase atoms object:" + "Uploading configurations one-by-one directly from an ase atoms object:" ] }, {