From de305a5fc45f0761a1c101ead9572614a447b764 Mon Sep 17 00:00:00 2001
From: OPTIMADE Developers The aim of OPTIMADE is to develop a common API, compliant with the JSON:API 1.0 specification. This is to enable interoperability among databases that serve crystal structures and calculated properties of existing and hypothetical materials. This repository contains a library of tools for implementing and consuming OPTIMADE APIs using Python: This document, guides, and the full module API documentation can be found online at https://optimade.org/optimade-python-tools. In particular, documentation of the OPTIMADE API response data models (implemented here with pydantic) can be found online under OPTIMADE Data Models. The release history and changelog can be found in the changelog. Detailed installation instructions for different use cases (e.g., using the library or running a server) can be found in the installation documentation. The latest stable version of this package can be obtained from PyPI: The latest development version of this package can be obtained from the main branch of this repository: Each release of the All development of this package (bug reports, suggestions, feedback and pull requests) occurs in the optimade-python-tools GitHub repository. Contribution guidelines and tips for getting help can be found in the contributing notes. If you use this package to access or serve OPTIMADE data, we kindly request that you consider citing the following: Full Changelog Full Changelog Fixed bugs: Merged pull requests: Full Changelog Fixed bugs: Merged pull requests: Full Changelog Merged pull requests: Full Changelog Closed issues: Merged pull requests: Full Changelog Closed issues: Merged pull requests: Full Changelog Closed issues: Merged pull requests: Full Changelog Fixed bugs: Merged pull requests: Full Changelog Fixed bugs: Closed issues: Full Changelog Full Changelog Implemented enhancements: Merged pull requests: Full Changelog Closed issues: Merged pull requests: Full Changelog optimade-python-tools has reached v1.0.0! We have decided to make this first major release at this point due to the arduous migration to pydantic v2 between v1.0.0 and v0.25.3. This will allow for improved compatibility with the rest of the ecosystem, plus all of the performance and ergonomics benefits of the new pydantic. If you are using optimade-python-tools primarily as a library, then you may need to make some code changes to support the new version. For example, the underlying API for dealing with the pydantic models has changed (e.g., Going forward, v1.0.x will be the last series to support v1.1 of the OPTIMADE specification, with future versions of the package v1.x.y adding features from the pre-release of OPTIMADE v1.2. A branch will be maintained for the v0.25.x series will be maintained for those who are stuck on pydantic v1 and run into bugs. Please make it clear in any bug reports if you are using this version. Fixed bugs: Closed issues: Merged pull requests: Full Changelog Merged pull requests: Full Changelog Merged pull requests: Full Changelog This release simply tests the migration to Merged pull requests: Full Changelog This release primarily contains changes to the server code including significant optimizations, the ability to fully disable validation of outgoing responses and the ability to configure provider fields that already exist in the database with a prefix, as well as various bug fixes and dependency compatibility updates. Other important changes include the dropping of Python 3.8 support. This version still uses Pydantic v1 and a future version will migrate fully to Pydantic v2. Fixed bugs: Closed issues: Merged pull requests: Full Changelog Implemented enhancements: Merged pull requests: Full Changelog Implemented enhancements: Closed issues: Merged pull requests: Full Changelog Merged pull requests: Full Changelog Fixed bugs: Merged pull requests: Full Changelog Closed issues: Merged pull requests: Full Changelog This patch release changes the synchronous functionality of Closed issues: Merged pull requests: Full Changelog This release adds a new feature to the reference server for avoiding validating data on the way out of the API (useful in cases where a small amount of leniency is desirable, and will have a minor performance bump). It also fixes a bug in the schema where Implemented enhancements: Fixed bugs: Full Changelog This minor release contains new client functionality and improved support for ASE. Ability to create OPTIMADE structure objects from ASE atoms:
+
+
Changelog¶
+v1.1.4 (2024-10-07)¶
+
v1.1.3 (2024-09-16)¶
v1.1.2 (2024-09-03)¶
diff --git a/latest/configuration/index.html b/latest/configuration/index.html
index e9fadf344..beba33967 100644
--- a/latest/configuration/index.html
+++ b/latest/configuration/index.html
@@ -2751,7 +2751,7 @@ List of configuration options
"base_url": null,
"implementation": {
"name": "OPTIMADE Python Tools",
- "version": "1.1.3",
+ "version": "1.1.4",
"source_url": "https://github.com/Materials-Consortia/optimade-python-tools",
"maintainer": {"email": "dev@optimade.org"}
},
diff --git a/latest/openapi/index_openapi.json b/latest/openapi/index_openapi.json
index 48fba7a06..9c4df1437 100644
--- a/latest/openapi/index_openapi.json
+++ b/latest/openapi/index_openapi.json
@@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "OPTIMADE API - Index meta-database",
- "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\nThis is the \"special\" index meta-database.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v1.1.3) v1.1.3.",
+ "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\nThis is the \"special\" index meta-database.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v1.1.4) v1.1.4.",
"version": "1.1.0"
},
"paths": {
diff --git a/latest/openapi/openapi.json b/latest/openapi/openapi.json
index fd3c225e5..c06693278 100644
--- a/latest/openapi/openapi.json
+++ b/latest/openapi/openapi.json
@@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "OPTIMADE API",
- "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v1.1.3) v1.1.3.",
+ "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v1.1.4) v1.1.4.",
"version": "1.1.0"
},
"paths": {
diff --git a/latest/search/search_index.json b/latest/search/search_index.json
index 66ebca8b8..9a7ae5933 100644
--- a/latest/search/search_index.json
+++ b/latest/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#optimade-python-tools","title":"OPTIMADE Python tools","text":"Latest releaseBuild statusActivity
"},{"location":"#documentation","title":"Documentation","text":"optimade.adapters
module.optimade-get
) that can query multiple OPTIMADE providers concurrently with a given filter, at the command-line or from Python code.optimade-validator
) or used as a GitHub Action from optimade-validator-action; this validator is used to construct the providers dashboard.pip install optimade\n
"},{"location":"#supported-optimade-versions","title":"Supported OPTIMADE versions","text":"git clone https://github.com/Materials-Consortia/optimade-python-tools\n
optimade
package from this repository only targets one version of the OPTIMADE specification, summarised in the table below.optimade
requirements v1.0.0 optimade<=0.12.9
v1.1.0 optimade~=0.16
"},{"location":"#contributing-and-getting-help","title":"Contributing and Getting Help","text":"
"},{"location":"#links","title":"Links","text":"
"},{"location":"CHANGELOG/","title":"Changelog","text":""},{"location":"CHANGELOG/#v113-2024-09-16","title":"v1.1.3 (2024-09-16)","text":"openapi.json
and index_openapi.json
.openapi.json
(see also interactive JSON editor).openapi.json
specification.
"},{"location":"CHANGELOG/#v111-2024-07-20","title":"v1.1.1 (2024-07-20)","text":"main
#2136 (ml-evs)ad0e214
to e90caba
#2131 (dependabot[bot]).from_url
method to base adapter for easily loading a single entry #2122 (ml-evs)
"},{"location":"CHANGELOG/#v110-2024-07-18","title":"v1.1.0 (2024-07-18)","text":"
"},{"location":"CHANGELOG/#v106-2024-06-22","title":"v1.0.6 (2024-06-22)","text":"
"},{"location":"CHANGELOG/#v105-2024-06-11","title":"v1.0.5 (2024-06-11)","text":"--random-seed
for deterministic validation #2096 (merkys)
"},{"location":"CHANGELOG/#v104-2024-03-29","title":"v1.0.4 (2024-03-29)","text":"skip_ssl
flag/option to client #2086 (ml-evs)
"},{"location":"CHANGELOG/#v103-2024-01-30","title":"v1.0.3 (2024-01-30)","text":"Structure
converter #2021 (ml-evs)
requests
timeout when getting providers list #1955 (ml-evs)
"},{"location":"CHANGELOG/#v0255-2024-01-30","title":"v0.25.5 (2024-01-30)","text":"46c8d85
to ad0e214
#1948 (dependabot[bot])97c69be
to 46c8d85
#1946 (dependabot[bot])93d2b52
to 97c69be
#1941 (dependabot[bot])members: true
#1936 (ml-evs)
requests.get
in get_providers
#1954LinksResource
#939
"},{"location":"CHANGELOG/#v0254-2024-01-11","title":"v0.25.4 (2024-01-11)","text":"
data_returned
is not available #1924
"},{"location":"CHANGELOG/#v101-2023-12-04","title":"v1.0.1 (2023-12-04)","text":"d8322a4
to 93d2b52
#1926 (dependabot[bot])564a499
to d8322a4
#1909 (dependabot[bot])
"},{"location":"CHANGELOG/#v100-2023-11-09","title":"v1.0.0 (2023-11-09)","text":"model.dict()
is now model.model_dump()
) -- a full migration guide can be found in the pydantic docs. Additionally, any cases where the underlying JSON schemas were being modified may need to be updated; internally we are pretty much exclusively operating on the pydantic model objects without needing to modify the raw schemas anymore.
nullable
from schemas (for OpenAPI 3.1) #1814
"},{"location":"CHANGELOG/#v0253-2023-09-29","title":"v0.25.3 (2023-09-29)","text":"--version
#1848 (ml-evs)
"},{"location":"CHANGELOG/#v0252-2023-09-12","title":"v0.25.2 (2023-09-12)","text":"links->next
issues #1794 (ml-evs)mongo_count_timeout
to skip the global count per request #1757 (ml-evs)
"},{"location":"CHANGELOG/#v0251-2023-08-10","title":"v0.25.1 (2023-08-10)","text":"pyproject.toml
, as well as updating a few dependencies along the way.
"},{"location":"CHANGELOG/#v0250-2023-07-25","title":"v0.25.0 (2023-07-25)","text":"
elements_ratios
length alias bug #1713 (JPBergsma)
"},{"location":"CHANGELOG/#v0241-2023-06-07","title":"v0.24.1 (2023-06-07)","text":"[all_strict]
deps set that reads from requirements files #1719 (ml-evs)1e5d59d
to 564a499
#1696 (dependabot[bot])1e5d59d
to 564a499
#1692 (dependabot[bot])1e5d59d
to 564a499
#1674 (dependabot[bot])
--list-properties
and --search-property
functionality to client #1663 (ml-evs)
"},{"location":"CHANGELOG/#v0240-2023-05-19","title":"v0.24.0 (2023-05-19)","text":"54c4f05
to 1e5d59d
#1654 (dependabot[bot])1c3f0f1
to 1e5d59d
#1639 (dependabot[bot])
LENGTH
queries with validator #1637 (ml-evs)
"},{"location":"CHANGELOG/#v0231-2023-04-13","title":"v0.23.1 (2023-04-13)","text":"ae359c2
to 1c3f0f1
#1638 (dependabot[bot])54c4f05
to ae359c2
#1622 (dependabot[bot])
"},{"location":"CHANGELOG/#v0230-2023-03-28","title":"v0.23.0 (2023-03-28)","text":"
"},{"location":"CHANGELOG/#v0222-2023-03-16","title":"v0.22.2 (2023-03-16)","text":"page_above
in base entry collections #1560 (ml-evs)
--skip-optional
#1558
"},{"location":"CHANGELOG/#v0221-2023-02-28","title":"v0.22.1 (2023-02-28)","text":"--skip-optional
#1559 (ml-evs)a92e5bc
to 3be73e9
#1538 (dependabot[bot])OptimadeClient
to use \"vanilla\" requests rather than httpx. This enables easier use inside of Jupyter notebooks.
OptimadeClient
in async mode with an existing event loop #1195
"},{"location":"CHANGELOG/#v0220-2023-02-27","title":"v0.22.0 (2023-02-27)","text":"OptimadeClient
with requests #1536 (ml-evs)page_below
and page_above
were codified as integers rather than strings.
"},{"location":"CHANGELOG/#v0210-2023-02-20","title":"v0.21.0 (2023-02-20)","text":"page_above/below
strings rather than ints #1529 (ml-evs)
from optimade.client import OptimadeClient\n\nDATABASE = pymongo.MongoClient().database.collection\n\ndef save_callback(url, results) -> None:\n for structure in results[\"data\"]:\n DATABASE.insert_one(structure)\n\nclient = OptimadeClient(callbacks=[save_callback])\nclient.get()\n
from optimade.adapters import Structure\nfrom ase import Atoms\n\nco = Atoms('CO', positions=[(0, 0, 0), (0, 0, 1.1)])\n\nstructure = Structure.from_ase_atoms(co)\n
Added ability to mute the client progress bars with --silent
/silent=True
and increased default response timeouts to better reflect those required for practical queries.
Implemented enhancements:
response_fields
behaviour #1514 (ml-evs)Closed issues:
Merged pull requests:
--silent
option to suppress client output until results #1518 (ml-evs)Full Changelog
This release primarily adds compatibility for the newest FastAPI releases (>=0.87
) by updating our test client to work with both httpx
and requests
.
Merged pull requests:
httpx
-based TestClient
for latest FastAPI version #1460 (ml-evs)Full Changelog
This release adds the ability to include or exclude particular providers from queries with the OptimadeClient
class and optimade-get
CLI, via the provider's registered prefix (#1412)
For example:
# Only query databases served by the example providers\noptimade-get --include-providers exmpl,optimade\n# Exclude example providers from global list\noptimade-get --exclude-providers exmpl,optimade\n
You can also now exclude particular databases by their URLs:
# Exclude specific example databases\noptimade-get --exclude-databases https://example.org/optimade,https://optimade.org/example\n
The release also includes some server enhancements and fixes: - Caching of /info/
and /info/<entry>
endpoint responses (#1433) - A bugfix for the entry mapper classes, which were sharing cache resources globally leading to poor utilization (#1435)
Implemented enhancements:
/info
and /info/<entry>
responses #1433 (ml-evs)Fixed bugs:
lru_cache
s on the mapper classes are subtly wrong #1434Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
get_child_databases
retriever #1411 (ml-evs)Full Changelog
This release continues the modularisation of the package by moving the server exceptions and warnings out into top-level modules, and removing the core dependency on FastAPI (now a server dependency only). This should allow for easier use of the optimade.models
and optimade.client
modules within other packages.
Aside from that, the package now supports Python 3.11, and our example server is now deployed at Fly.io rather than Heroku.
Implemented enhancements:
Fixed bugs:
Closed issues:
page_number
#1372Merged pull requests:
page_number
at model level #1369 (ml-evs)mypy
and isort
in pre-commit & CI #1346 (ml-evs)Full Changelog
This is a hotfix release for #1335, a bug regarding chunked responses triggered when using the latest FastAPI version.
Fixed bugs:
chunk_size
is not always set in middleware method #1335chunk_size
is properly set when chunking responses #1336 (ml-evs)Full Changelog
Implemented enhancements:
meta->schema
value automatically #1323 (ml-evs)Merged pull requests:
Full Changelog
Fixed bugs:
page_number
query parameter #1303 (ml-evs)Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
from_pymatgen
structure adapter method and concept of ingesters #1296 (ml-evs)lru_cache
to many mapper properties #1245 (ml-evs)Merged pull requests:
Full Changelog
This minor release includes several usability improvements for the server and client arising from the OPTIMADE workshop. This release also drops support for Python 3.7, which should allow us to streamline our dependencies going forward.
Implemented enhancements:
Fixed bugs:
@classproperty
#1219Closed issues:
meta->schema
#1209meta->schemas
field to reference server #1208Merged pull requests:
fb05359
to a92e5bc
#1267 (dependabot[bot])lark
dependency to new name #1231 (ml-evs)schema_url
and index_schema_url
options #1210 (ml-evs)Full Changelog
This is a feature release that includes the new optimade.client.OptimadeClient
class, a client capable asynchronously querying multiple OPTIMADE APIs simultaneously. It also contains a patch for the OPTIMADE models that allows them to be used with more recent FastAPI versions without breaking OpenAPI 3.0 compatibility. Other changes can be found below. This release includes improvements to the validator to catch more cases where OPTIMADE APIs are only partially implemented. Previously, APIs that did not support filtering, pagination or limiting response fields at all (i.e., the query parameter is simply ignored) would pass most validation tests erroneously in some unlucky situations (#1180).
Implemented enhancements:
User-Agent
header #1187Fixed bugs:
OptimadeClient
crashes if an index meta-database is down #1196Merged pull requests:
User-Agent
with validator #1189 (ml-evs)fastapi>0.66
#1131 (ml-evs)Full Changelog
This release includes improvements to the validator to catch more cases where OPTIMADE APIs are only partially implemented. Previously, APIs that did not support filtering, pagination or limiting response fields at all (i.e., the query parameter is simply ignored) would pass most validation tests erroneously in some unlucky situations (#1180).
Fixed bugs:
Merged pull requests:
Full Changelog
This patch release adds a pre-built Docker container for the reference server to the GitHub Container Registry (GHCR) and a series of Deployment instructions in the online documentation.
The image can be easily pulled from GHCR with:
docker pull ghcr.io/materials-consortia/optimade
Implemented enhancements:
Closed issues:
Merged pull requests:
env
context for step #1178 (CasperWA)optimade
on ghcr.io #1171 (CasperWA)Full Changelog
This minor release contains fixes recommended for those deploying the optimade-python-tools reference server:
meta->data_returned
field was previously incorrect when using the MongoDB backend.validate_query_parameters
config option.nelements != 2
vs 2 != nelements
); this has now been fixed.Implemented enhancements:
immutable_id
#1142 (ml-evs)Fixed bugs:
meta->data_returned
is incorrect for paginated results with MongoDB #1140meta->data_returned
for paginated results with MongoDB #1141 (ml-evs)Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
species
from species_at_sites
when missing #1103 (ml-evs)Closed issues:
Full Changelog
Merged pull requests:
Full Changelog
Fixed bugs:
Security fixes:
Merged pull requests:
build
package to build distributions #1062 (CasperWA)Full Changelog
Implemented enhancements:
Security fixes:
Closed issues:
LarkParser
failing. #1037Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
chemical_formula_anonymous
validator accepts incorrect proportion order if started with 1 #1002Closed issues:
Merged pull requests:
chemical_formula_anonymous = AB2
to pass validation. #1001 (JPBergsma)diff
for checking PR body #1000 (CasperWA)Full Changelog
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
master
#971 (CasperWA)Full Changelog
Closed issues:
Merged pull requests:
357c27b
to fb05359
#945 (dependabot[bot])368f9f6
to 357c27b
#944 (dependabot[bot])91b51bd
to 368f9f6
#942 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
run.sh
does not appear to be available from the pip installation #904Merged pull requests:
Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
handle_response_fields()
#876 (CasperWA)Fixed bugs:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
fa25ed3
to 91b51bd
#858 (dependabot[bot])Full Changelog
Implemented enhancements:
Closed issues:
Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Implemented enhancements:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
7a54843
to fa25ed3
#791 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
alias_of
extracts alias wrongly #667Closed issues:
Merged pull requests:
e2074e8
to 7a54843
#741 (dependabot[bot])Full Changelog
Fixed bugs:
Merged pull requests:
da74513
to e2074e8
#727 (dependabot[bot])Full Changelog
Implemented enhancements:
Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
--as-type
in validator, one does not get a summary (--json
doesn't work) #699Merged pull requests:
Full Changelog
Implemented enhancements:
structure_features
#678Fixed bugs:
meta->data_available
#677Merged pull requests:
542ac0a
to da74513
#679 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Content-Type
value should be granularized according to RFC requirements in validator #669Merged pull requests:
fe5048b
to 542ac0a
#671 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
2673be6
to fe5048b
#633 (dependabot[bot])Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Closed issues:
Merged pull requests:
validatated
-> validated
#506 (merkys)Full Changelog
Fixed bugs:
Closed issues:
/links
endpoint for index meta-database #454data_returned
#402Merged pull requests:
/links
-endpoints #455 (CasperWA)Full Changelog
Implemented enhancements:
warnings
#105Fixed bugs:
/versions
endpoint content-type parameter \"header=present\" is provided in the wrong place #418Closed issues:
api_hint
query parameter #392Merged pull requests:
OptimadeWarning
s #431 (CasperWA)52027b1
to 9712dd8
#428 (dependabot[bot])api_hint
query parameter #421 (CasperWA)a96d424
to 52027b1
#389 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Merged pull requests:
Full Changelog
"},{"location":"CHANGELOG/#v096-2020-06-28","title":"v0.9.6 (2020-06-28)","text":"Full Changelog
Fixed bugs:
Full Changelog
"},{"location":"CHANGELOG/#v094-2020-06-26","title":"v0.9.4 (2020-06-26)","text":"Full Changelog
"},{"location":"CHANGELOG/#v093-2020-06-26","title":"v0.9.3 (2020-06-26)","text":"Full Changelog
Merged pull requests:
732593a
to a96d424
#368 (dependabot[bot])Full Changelog
Fixed bugs:
Closed issues:
type
field under properties
) #345Merged pull requests:
Full Changelog
"},{"location":"CHANGELOG/#v090-2020-06-17","title":"v0.9.0 (2020-06-17)","text":"Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
sortable
key under each property #273source_url
#260None
values in lattice_vectors
#170Merged pull requests:
sortable=True
to all properties #274 (CasperWA)Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
deps_clean-install
#247server.cfg
? #134Merged pull requests:
~
#246 (CasperWA)Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
include
standard JSON API query parameter #94Merged pull requests:
include
query parameter #163 (CasperWA)Full Changelog
Implemented enhancements:
Fixed bugs:
response_fields
not working #154Closed issues:
page_page
to page_number
#165Merged pull requests:
Full Changelog
Implemented enhancements:
develop
or not? Default branch? - Create INSTALL.md #136Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Merged pull requests:
JSONDecodeError
s in validator #144 (ml-evs)Full Changelog
Implemented enhancements:
Fixed bugs:
load_from_json
#137 (CasperWA)Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Implemented enhancements:
implementation
in top-level meta response #117Fixed bugs:
update-openapijson
is incomplete #123Closed issues:
sortable
field must be added to models #84Merged pull requests:
\"all\"
endpoint and rename collections submodule #73 (ml-evs)pre-commit
tool #53 (dwinston)Full Changelog
"},{"location":"CHANGELOG/#v011-2018-06-13","title":"v0.1.1 (2018-06-13)","text":"Full Changelog
"},{"location":"CHANGELOG/#v010-2018-06-05","title":"v0.1.0 (2018-06-05)","text":"Full Changelog
* This Changelog was automatically generated by github_changelog_generator
"},{"location":"CONTRIBUTING/","title":"Contributing and getting help","text":"If you run into any problems using this package, or if you have a question, suggestion or feedback, then please raise an issue on GitHub.
The Materials Consortia is very open to contributions across all of its packages. This may be anything from simple feedback and raising new issues to creating new PRs.
If you are interested in contributing but don't know where to begin, some issues have been marked with the good first issue label, typically where an isolated enhancement has a concrete suggestion. Simply add a comment under an issue if you are interested in tackling it!
Recommendations for setting up a development environment for this package can be found in the Installation instructions.
More broadly, if you would like to ask questions or contact the consortium about creating an OPTIMADE implementation for a new database, then please read the relevant \"get involved\" section on the OPTIMADE website.
"},{"location":"INSTALL/","title":"Installation","text":"This package can be installed from PyPI, or by cloning the repository, depending on your use-case.
optimade
Python package as a library, (e.g., using the models for validation, parsing filters with the grammar, or using the command-line tool optimade-validator
tool), it is recommended that you install the latest release of the package from PyPI with pip install optimade
. If you also want to use the OPTIMADE client to query OPTIMADE APIs, you should install with the additional dependencies: pip install optimade[http_client]
.pip install .
, or pip install -e .
for an editable installation). As an alternative, you can run the optimade
container image (see the Container image section below).This package may be used to setup and run an OPTIMADE index meta-database. Clone this repository and install the package locally with pip install -e .[server]
.
Info
To avoid installing anything locally and instead use the docker image, please see the section Container image below.
There is a built-in index meta-database set up to populate a mongomock
in-memory database with resources from a static json
file containing the child
resources you, as a database provider, want to serve under this index meta-database. The location of that json
file is controllable using the index_links_path
property of the configuration or setting via the environment variable optimade_index_links_path
.
Running the index meta-database is then as simple as writing ./run.sh index
in a terminal from the root of this package. You can find it at the base URL: http://localhost:5001/v1.
Here is an example of how it may look to start your server:
export OPTIMADE_CONFIG_FILE=/home/optimade_server/config.json\n./path/to/optimade/run.sh index\n
"},{"location":"INSTALL/#full-development-installation","title":"Full development installation","text":"The dependencies of this package can be found in setup.py
with their latest supported versions. By default, a minimal set of requirements are installed to work with the filter language and the pydantic
models. After cloning the repository, the install mode server
(i.e. pip install .[server]
) is sufficient to run a uvicorn
server using the mongomock
backend (or MongoDB with pymongo
, if present). The suite of development and testing tools are installed with via the install modes dev
and testing
. There are additionally two backend-specific install modes, elastic
and mongo
, as well as the all
mode, which installs all dependencies. All contributed Python code, must use the black code formatter, and must pass the flake8 linter that is run automatically on all PRs.
# Clone this repository to your computer\ngit clone --recursive git@github.com:Materials-Consortia/optimade-python-tools.git\ncd optimade-python-tools\n\n# Ensure a Python>=3.8 (virtual) environment (example below using Anaconda/Miniconda)\nconda create -n optimade python=3.10\nconda activate optimade\n\n# Install package and dependencies in editable mode (including \"dev\" requirements).\npip install -e \".[dev]\"\n\n# Optional: Retrieve the list of OPTIMADE providers. (Without this submodule, some of the tests will fail because \"providers.json\" cannot be found.)\ngit submodule update --init\n\n# Run the tests with pytest\npy.test\n\n# Install pre-commit environment (e.g., auto-formats code on `git commit`)\npre-commit install\n\n# Optional: Install MongoDB (and set `database_backend = mongodb`)\n# Below method installs in conda environment and\n# - starts server in background\n# - ensures and uses ~/dbdata directory to store data\nconda install -c anaconda mongodb\nmkdir -p ~/dbdata && mongod --dbpath ~/dbdata --syslog --fork\n\n# Start a development server (auto-reload on file changes at http://localhost:5000\n# You can also execute ./run.sh\nuvicorn optimade.server.main:app --reload --port 5000\n\n# View auto-generated docs\nopen http://localhost:5000/docs\n# View Open API Schema\nopen http://localhost:5000/openapi.json\n
When developing, you can run both the server and an index meta-database server at the same time (from two separate terminals). Running the following:
./run.sh index\n# or\nuvicorn optimade.server.main_index:app --reload --port 5001\n
will run the index meta-database server at http://localhost:5001/v1.
"},{"location":"INSTALL/#testing-specific-backends","title":"Testing specific backends","text":"In order to run the test suite for a specific backend, the OPTIMADE_DATABASE_BACKEND
environment variable (or config option) can be set to one of 'mongodb'
, 'mongomock'
or 'elastic'
(see ServerConfig.database_backend
). Tests for the two \"real\" database backends, MongoDB and Elasticsearch, require a writable, temporary database to be accessible.
The easiest way to deploy these databases and run the tests is with Docker, as shown below. Docker installation instructions will depend on your system; on Linux, the docker
commands below may need to be prepended with sudo
, depending on your distribution. These commands should be run from a local optimade-python-tools directory.
The following command starts a local Elasticsearch v7 instance, runs the test suite, then stops and deletes the containers (required as the tests insert some data):
docker run -d --name elasticsearch_test -p 9200:9200 -p 9300:9300 -e \"discovery.type=single-node\" -e \"xpack.security.enabled=false\" elasticsearch:7.17.7 \\\n&& sleep 20 \\\n&& OPTIMADE_DATABASE_BACKEND=\"elastic\" py.test; \\\ndocker container stop elasticsearch_test; docker container rm elasticsearch_test\n
The following command starts a local MongoDB instance, runs the test suite, then stops and deletes the containers:
docker run -d --name mongo_test -p 27017:27017 -d mongo:4.4.6 \\\n&& OPTIMADE_DATABASE_BACKEND=\"mongodb\" py.test; \\\ndocker container stop mongo_test; docker container rm mongo_test\n
"},{"location":"INSTALL/#container-image","title":"Container image","text":""},{"location":"INSTALL/#retrieve-the-image","title":"Retrieve the image","text":"The optimade
container image is available from the GitHub Container registry. To pull the latest version using Docker run the following:
docker pull ghcr.io/materials-consortia/optimade:latest\n
Note
The tag, :latest
, can be left out, as the latest
version will be pulled by default.
If you'd like to pull a specific version, this can be done by replacing latest
in the command above with the version of choice, e.g., 0.17.1
. To see which versions are available, please go here.
You can also install the develop
version. This is an image built from the latest commit on the main
branch and should never be used for production.
When starting a container from the image there are a few choices. It is possible to run either a standard OPTIMADE server, or an index meta-database server from this image. Note, these servers can be run in separate containers at the same time. The key is setting the environment variable MAIN
.
main
Standard OPTIMADE server. main_index
Index meta-database OPTIMADE server. Using Docker, the following command will run a container from the image:
# rm will remove container when it exits.\n# detach will run the server in the background.\n# publish will run the server from the host port 8080.\n# name will give the container a handy name for referencing later.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n ghcr.io/materials-consortia/optimade:latest\n
The server should now be available at localhost:8080.
"},{"location":"all_models/","title":"OPTIMADE Data Models","text":"This page provides documentation for the optimade.models
submodule, where all the OPTIMADE (and JSON:API)-defined data models are located.
For example, the three OPTIMADE entry types, structures
, references
and links
, are defined primarily through the corresponding attribute models:
StructureResourceAttributes
ReferenceResourceAttributes
LinksResourceAttributes
As well as validating data types when creating instances of these models, this package defines several OPTIMADE-specific validators that ensure consistency between fields (e.g., the value of nsites
matches the number of positions provided in cartesian_site_positions
).
ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"all_models/#optimade.models.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"all_models/#optimade.models.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"all_models/#optimade.models.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"all_models/#optimade.models.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"all_models/#optimade.models.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"all_models/#optimade.models.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"all_models/#optimade.models.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"all_models/#optimade.models.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"all_models/#optimade.models.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"all_models/#optimade.models.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"all_models/#optimade.models.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"all_models/#optimade.models.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"all_models/#optimade.models.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"all_models/#optimade.models.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"all_models/#optimade.models.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"all_models/#optimade.models.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"all_models/#optimade.models.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"all_models/#optimade.models.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"all_models/#optimade.models.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"all_models/#optimade.models.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo","title":"baseinfo
","text":""},{"location":"all_models/#optimade.models.baseinfo.VERSIONED_BASE_URL_PATTERN","title":"VERSIONED_BASE_URL_PATTERN = '^.+/v[0-1](\\\\.[0-9]+)*/?$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries","title":"entries
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.entries.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship","title":"ReferenceRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class ReferenceRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"references\"]] = \"references\"\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship","title":"StructureRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class StructureRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"structures\"]] = \"structures\"\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship","title":"TypedRelationship
","text":" Bases: Relationship
optimade/models/entries.py
class TypedRelationship(Relationship):\n _req_type: ClassVar[str]\n\n @field_validator(\"data\", mode=\"after\")\n @classmethod\n def check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n ) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.index_metadb","title":"index_metadb
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi","title":"jsonapi
","text":"This module should reproduce JSON API v1.0 https://jsonapi.org/format/1.0/
"},{"location":"all_models/#optimade.models.jsonapi.JsonLinkType","title":"JsonLinkType = Union[AnyUrl, Link]
module-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"all_models/#optimade.models.jsonapi.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error","title":"Error
","text":" Bases: BaseModel
An error response
Source code inoptimade/models/jsonapi.py
class Error(BaseModel):\n \"\"\"An error response\"\"\"\n\n id: Annotated[\n Optional[str],\n StrictField(\n description=\"A unique identifier for this particular occurrence of the problem.\",\n ),\n ] = None\n links: Annotated[\n Optional[ErrorLinks], StrictField(description=\"A links object storing about\")\n ] = None\n status: Annotated[\n Optional[Annotated[str, BeforeValidator(str)]],\n StrictField(\n description=\"the HTTP status code applicable to this problem, expressed as a string value.\",\n ),\n ] = None\n code: Annotated[\n Optional[str],\n StrictField(\n description=\"an application-specific error code, expressed as a string value.\",\n ),\n ] = None\n title: Annotated[\n Optional[str],\n StrictField(\n description=\"A short, human-readable summary of the problem. \"\n \"It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.\",\n ),\n ] = None\n detail: Annotated[\n Optional[str],\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ] = None\n source: Annotated[\n Optional[ErrorSource],\n StrictField(\n description=\"An object containing references to the source of the error\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the error.\",\n ),\n ] = None\n\n def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.jsonapi.Error.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.detail","title":"detail: Annotated[Optional[str], StrictField(description='A human-readable explanation specific to this occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"all_models/#optimade.models.jsonapi.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship","title":"Relationship
","text":" Bases: BaseModel
Representation references from the resource object in which it's defined to other resource objects.
Source code inoptimade/models/jsonapi.py
class Relationship(BaseModel):\n \"\"\"Representation references from the resource object in which it's defined to other resource objects.\"\"\"\n\n links: Annotated[\n Optional[RelationshipLinks],\n StrictField(\n description=\"a links object containing at least one of the following: self, related\",\n ),\n ] = None\n data: Annotated[\n Optional[Union[BaseResource, list[BaseResource]]],\n StrictField(description=\"Resource linkage\"),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object that contains non-standard meta-information about the relationship.\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationship.data","title":"data: Annotated[Optional[Union[BaseResource, list[BaseResource]]], StrictField(description='Resource linkage')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"all_models/#optimade.models.jsonapi.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.jsonapi.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.resource_json_schema_extra","title":"resource_json_schema_extra(schema, model)
","text":"Ensure id
and type
are the first two entries in the list required properties.
This requires that id
and type
are the first model fields defined for all sub-models of BaseResource
.
optimade/models/jsonapi.py
def resource_json_schema_extra(\n schema: dict[str, Any], model: type[\"BaseResource\"]\n) -> None:\n \"\"\"Ensure `id` and `type` are the first two entries in the list required properties.\n\n Note:\n This _requires_ that `id` and `type` are the _first_ model fields defined\n for all sub-models of `BaseResource`.\n\n \"\"\"\n if \"id\" not in schema.get(\"required\", []):\n schema[\"required\"] = [\"id\"] + schema.get(\"required\", [])\n if \"type\" not in schema.get(\"required\", []):\n required = []\n for field in schema.get(\"required\", []):\n required.append(field)\n if field == \"id\":\n # To make sure the property order match the listed properties,\n # this ensures \"type\" is added immediately after \"id\".\n required.append(\"type\")\n schema[\"required\"] = required\n
"},{"location":"all_models/#optimade.models.links","title":"links
","text":""},{"location":"all_models/#optimade.models.links.Aggregate","title":"Aggregate
","text":" Bases: Enum
Enumeration of aggregate values
Source code inoptimade/models/links.py
class Aggregate(Enum):\n \"\"\"Enumeration of aggregate values\"\"\"\n\n OK = \"ok\"\n TEST = \"test\"\n STAGING = \"staging\"\n NO = \"no\"\n
"},{"location":"all_models/#optimade.models.links.Aggregate.NO","title":"NO = 'no'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.OK","title":"OK = 'ok'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.STAGING","title":"STAGING = 'staging'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.TEST","title":"TEST = 'test'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType","title":"LinkType
","text":" Bases: Enum
Enumeration of link_type values
Source code inoptimade/models/links.py
class LinkType(Enum):\n \"\"\"Enumeration of link_type values\"\"\"\n\n CHILD = \"child\"\n ROOT = \"root\"\n EXTERNAL = \"external\"\n PROVIDERS = \"providers\"\n
"},{"location":"all_models/#optimade.models.links.LinkType.CHILD","title":"CHILD = 'child'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.EXTERNAL","title":"EXTERNAL = 'external'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.PROVIDERS","title":"PROVIDERS = 'providers'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.ROOT","title":"ROOT = 'root'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.links.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.links.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json","title":"optimade_json
","text":"Modified JSON API v1.0 for OPTIMADE API
"},{"location":"all_models/#optimade.models.optimade_json.ValidIdentifier","title":"ValidIdentifier = Annotated[str, Field(pattern=IDENTIFIER_REGEX)]
module-attribute
","text":"A type that constrains strings to valid OPTIMADE identifiers (e.g., property names, ID strings).
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"all_models/#optimade.models.optimade_json.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.optimade_json.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.optimade_json.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.warnings_json_schema_extra","title":"warnings_json_schema_extra(schema, model)
","text":"Update OpenAPI JSON schema model for Warning
.
type
is in the list required properties and in the correct place.status
property. This property is not allowed for Warning
, nor is it a part of the OPTIMADE definition of the Warning
object.Since type
is the last model field defined, it will simply be appended.
optimade/models/optimade_json.py
def warnings_json_schema_extra(schema: dict[str, Any], model: type[\"Warnings\"]) -> None:\n \"\"\"Update OpenAPI JSON schema model for `Warning`.\n\n * Ensure `type` is in the list required properties and in the correct place.\n * Remove `status` property.\n This property is not allowed for `Warning`, nor is it a part of the OPTIMADE\n definition of the `Warning` object.\n\n Note:\n Since `type` is the _last_ model field defined, it will simply be appended.\n\n \"\"\"\n if \"required\" in schema:\n if \"type\" not in schema[\"required\"]:\n schema[\"required\"].append(\"type\")\n else:\n schema[\"required\"] = [\"type\"]\n schema.get(\"properties\", {}).pop(\"status\", None)\n
"},{"location":"all_models/#optimade.models.references","title":"references
","text":""},{"location":"all_models/#optimade.models.references.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.references.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.responses","title":"responses
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.responses.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.structures","title":"structures
","text":""},{"location":"all_models/#optimade.models.structures.CORRELATED_STRUCTURE_FIELDS","title":"CORRELATED_STRUCTURE_FIELDS = ({'dimension_types', 'nperiodic_dimensions'}, {'cartesian_site_positions', 'species_at_sites'}, {'nsites', 'cartesian_site_positions'}, {'species_at_sites', 'species'})
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.EPS","title":"EPS = 2 ** -23
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Vector3D_unknown","title":"Vector3D_unknown = Annotated[list[Optional[Annotated[float, BeforeValidator(float)]]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"all_models/#optimade.models.structures.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"all_models/#optimade.models.structures.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"all_models/#optimade.models.structures.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"all_models/#optimade.models.structures.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"all_models/#optimade.models.types","title":"types
","text":""},{"location":"all_models/#optimade.models.types.AnnotatedType","title":"AnnotatedType = type(ChemicalSymbol)
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.ChemicalSymbol","title":"ChemicalSymbol = Annotated[str, Field(pattern=EXTENDED_CHEMICAL_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.ElementSymbol","title":"ElementSymbol = Annotated[str, Field(pattern=ELEMENT_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.NoneType","title":"NoneType = type(None)
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.OptionalType","title":"OptionalType = type(Optional[str])
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.SemanticVersion","title":"SemanticVersion = Annotated[str, Field(pattern=SEMVER_PATTERN, examples=['0.10.1', '1.0.0-rc.2', '1.2.3-rc.5+develop'])]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.UnionType","title":"UnionType = type(Union[str, int])
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils","title":"utils
","text":""},{"location":"all_models/#optimade.models.utils.ANONYMOUS_ELEMENTS","title":"ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150))
module-attribute
","text":"Returns the first 150 values of the anonymous element generator.
"},{"location":"all_models/#optimade.models.utils.ATOMIC_NUMBERS","title":"ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.CHEMICAL_FORMULA_REGEXP","title":"CHEMICAL_FORMULA_REGEXP = '(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\\\d+)?)+$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.ELEMENT_SYMBOLS_PATTERN","title":"ELEMENT_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.EXTENDED_CHEMICAL_SYMBOLS_PATTERN","title":"EXTENDED_CHEMICAL_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS + EXTRA_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.IDENTIFIER_REGEX","title":"IDENTIFIER_REGEX = '^[a-z_][a-z_0-9]+$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_KEYS","title":"OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_PREFIX","title":"OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SEMVER_PATTERN","title":"SEMVER_PATTERN = '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"all_models/#optimade.models.utils.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OptimadeField","title":"OptimadeField(default=PydanticUndefined, *, support=None, queryable=None, unit=None, **kwargs)
","text":"A wrapper around pydantic.Field
that adds OPTIMADE-specific field paramters queryable
, support
and unit
, indicating the corresponding support level in the specification and the physical unit of the field.
Parameters:
Name Type Description Defaultsupport
Optional[Union[str, SupportLevel]]
The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.
None
queryable
Optional[Union[str, SupportLevel]]
The support level corresponding to the queryablility of this field.
None
unit
Optional[str]
A string describing the unit of the field.
None
Returns:
Type DescriptionAny
The pydantic field with extra validation provided by StrictField
.
optimade/models/utils.py
def OptimadeField(\n default: \"Any\" = PydanticUndefined,\n *,\n support: Optional[Union[str, SupportLevel]] = None,\n queryable: Optional[Union[str, SupportLevel]] = None,\n unit: Optional[str] = None,\n **kwargs,\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that adds OPTIMADE-specific\n field paramters `queryable`, `support` and `unit`, indicating\n the corresponding support level in the specification and the\n physical unit of the field.\n\n Arguments:\n support: The support level of the field itself, i.e. whether the field\n can be null or omitted by an implementation.\n queryable: The support level corresponding to the queryablility\n of this field.\n unit: A string describing the unit of the field.\n\n Returns:\n The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].\n\n \"\"\"\n\n # Collect non-null keyword arguments to add to the Field schema\n if unit is not None:\n kwargs[\"unit\"] = unit\n\n if queryable is not None:\n if isinstance(queryable, str):\n queryable = SupportLevel(queryable.lower())\n kwargs[\"queryable\"] = queryable\n\n if support is not None:\n if isinstance(support, str):\n support = SupportLevel(support.lower())\n kwargs[\"support\"] = support\n\n return StrictField(default, **kwargs)\n
"},{"location":"all_models/#optimade.models.utils.StrictField","title":"StrictField(default=PydanticUndefined, *, description=None, **kwargs)
","text":"A wrapper around pydantic.Field
that does the following:
pydantic.Field
, except those used elsewhere to modify the schema in-place, e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g. \"unit\", \"queryable\" and \"sortable\".Parameters:
Name Type Description Defaultdefault
Any
The only non-keyword argument allowed for Field.
PydanticUndefined
description
Optional[str]
The description of the Field
; if this is not specified then a UserWarning
will be emitted.
None
**kwargs
Any
Extra keyword arguments to be passed to Field
.
{}
Raises:
Type DescriptionRuntimeError
If **kwargs
contains a key not found in the function signature of Field
, or in the extensions used by models in this package (see above).
Returns:
Type DescriptionAny
The pydantic Field
.
optimade/models/utils.py
def StrictField(\n default: \"Any\" = PydanticUndefined,\n *,\n description: Optional[str] = None,\n **kwargs: \"Any\",\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that does the following:\n\n - Forbids any \"extra\" keys that would be passed to `pydantic.Field`,\n except those used elsewhere to modify the schema in-place,\n e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g.\n \"unit\", \"queryable\" and \"sortable\".\n - Emits a warning when no description is provided.\n\n Arguments:\n default: The only non-keyword argument allowed for Field.\n description: The description of the `Field`; if this is not\n specified then a `UserWarning` will be emitted.\n **kwargs: Extra keyword arguments to be passed to `Field`.\n\n Raises:\n RuntimeError: If `**kwargs` contains a key not found in the\n function signature of `Field`, or in the extensions used\n by models in this package (see above).\n\n Returns:\n The pydantic `Field`.\n\n \"\"\"\n allowed_schema_and_field_keys = [\"pattern\"]\n\n allowed_keys = [\n \"pattern\",\n \"uniqueItems\",\n ] + OPTIMADE_SCHEMA_EXTENSION_KEYS\n _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]\n\n if _banned:\n raise RuntimeError(\n f\"Not creating StrictField({default!r}, **{kwargs!r}) with \"\n f\"forbidden keywords {_banned}.\"\n )\n\n # Handle description\n if description is None:\n warnings.warn(\n f\"No description provided for StrictField specified by {default!r}, \"\n f\"**{kwargs!r}.\"\n )\n else:\n kwargs[\"description\"] = description\n\n # OPTIMADE schema extensions\n json_schema_extra: dict[str, Any] = kwargs.pop(\"json_schema_extra\", {})\n\n # Go through all JSON Schema keys and add them to the json_schema_extra.\n for key in allowed_keys:\n if key not in kwargs:\n continue\n\n # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.\n schema_key = (\n f\"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}\"\n if key in OPTIMADE_SCHEMA_EXTENSION_KEYS\n else key\n )\n\n for key_variant in (key, schema_key):\n if key_variant in json_schema_extra:\n if json_schema_extra.pop(key_variant) != kwargs[key]:\n raise RuntimeError(\n f\"Conflicting values for {key} in json_schema_extra and kwargs.\"\n )\n\n json_schema_extra[schema_key] = (\n kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)\n )\n\n kwargs[\"json_schema_extra\"] = json_schema_extra\n\n return Field(default, **kwargs)\n
"},{"location":"all_models/#optimade.models.utils.anonymize_formula","title":"anonymize_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and returns the OPTIMADE chemical_formula_anonymous
representation, i.e., a reduced chemical formula comprising of element symbols drawn from A, B, C... ordered from largest proportion to smallest.
Returns:
Type Descriptionstr
The anonymous chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def anonymize_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n returns the OPTIMADE `chemical_formula_anonymous` representation, i.e., a reduced chemical formula comprising of element symbols\n drawn from A, B, C... ordered from largest proportion to smallest.\n\n Returns:\n The anonymous chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=False, anonymize=True)\n
"},{"location":"all_models/#optimade.models.utils.anonymous_element_generator","title":"anonymous_element_generator()
","text":"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.
Source code inoptimade/models/utils.py
def anonymous_element_generator() -> \"Generator[str, None, None]\":\n \"\"\"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.\"\"\"\n from string import ascii_lowercase\n\n for size in itertools.count(1):\n for tuple_strings in itertools.product(ascii_lowercase, repeat=size):\n list_strings = list(tuple_strings)\n list_strings[0] = list_strings[0].upper()\n yield \"\".join(list_strings)\n
"},{"location":"all_models/#optimade.models.utils.reduce_formula","title":"reduce_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.
Returns:
Type Descriptionstr
The reduced chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def reduce_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.\n\n Returns:\n The reduced chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=True, anonymize=False)\n
"},{"location":"configuration/","title":"Configuration","text":"Since the server implementation is built with FastAPI, which uses pydantic, the configuration is based on pydantic's Setting management. This way of handling configuration options supports various different approaches to configure the server. We recommend either or a combination of the following:
OPTIMADE_CONFIG_FILE
environment variable.OPTIMADE_
or optimade_
.ServerConfig
object with the desired settings directly.The main way of configuring the OPTIMADE server is by creating a configuration JSON file.
An example of one that works with the example implementation can be found in optimade_config.json
:
{\n \"debug\": false,\n \"default_db\": \"test_server\",\n \"base_url\": \"http://localhost:5000\",\n \"implementation\": {\n \"name\": \"Example implementation\",\n \"source_url\": \"https://github.com/Materials-Consortia/optimade-python-tools\",\n \"issue_tracker\": \"https://github.com/Materials-Consortia/optimade-python-tools/issues\",\n \"homepage\": \"https://optimade.org/optimade-python-tools\",\n \"maintainer\": {\"email\": \"dev@optimade.org\"}\n },\n \"provider\": {\n \"name\": \"Example provider\",\n \"description\": \"Provider used for examples, not to be assigned to a real database\",\n \"prefix\": \"exmpl\",\n \"homepage\": \"https://example.com\"\n },\n \"index_base_url\": \"http://localhost:5001\",\n \"provider_fields\": {\n \"structures\": [\n \"band_gap\",\n {\"name\": \"chemsys\", \"type\": \"string\", \"description\": \"A string representing the chemical system in an ordered fashion\"}\n ]\n },\n \"aliases\": {\n \"structures\": {\n \"id\": \"task_id\",\n \"immutable_id\": \"_id\",\n \"chemical_formula_descriptive\": \"pretty_formula\",\n \"chemical_formula_reduced\": \"pretty_formula\",\n \"chemical_formula_anonymous\": \"formula_anonymous\"\n }\n },\n \"length_aliases\": {\n \"structures\": {\n \"chemsys\": \"nelements\"\n }\n }\n}\n
"},{"location":"configuration/#environment-variables","title":"Environment variables","text":"In order for the implementation to know where your configuration JSON file is located, you can set an environment variable OPTIMADE_CONFIG_FILE
with either the value of the absolute path to the configuration file or the relative path to the file from the current working directory of where the server is run.
This variable is actually an extension of the configuration option config_file
. By default, the server will try to load a JSON file called .optimade.json
located in your home folder (or equivalent).
Here the generally recognized environment variable prefix becomes evident, namely OPTIMADE_
or optimade_
. Hence, you can set (or overwrite) any configuration option from the server's defaults or a value read from the configuration JSON by setting an environment variable named OPTIMADE_<configuration_option>
.
One can extend the current list of configuration options by sub-classing ServerConfig
and adding configuration options as attributes with values of Field
(pydantic.field
). Any attribute type will be validated through pydantic
as is the case for all of the regular configuration options.
This is useful for, e.g., custom database backends, if one wants to utilize the general server configuration setup implemented in optimade
to declare specific database information. It can also be useful if one wishes to extend and build upon the general optimade
server with new endpoints and routes.
Remember to instantiate an instance of the sub-class, which can be imported and used in your application.
"},{"location":"configuration/#list-of-configuration-options","title":"List of configuration options","text":"See config.py
for a complete list of configuration options.
The following configuration file represents the default values for all configuration options:
Default values for all configuration options{\n \"debug\": false,\n \"insert_test_data\": true,\n \"mongo_database\": \"optimade\",\n \"mongo_uri\": \"localhost:27017\",\n \"links_collection\": \"links\",\n \"references_collection\": \"references\",\n \"structures_collection\": \"structures\",\n \"page_limit\": 20,\n \"page_limit_max\": 500,\n \"default_db\": \"test_server\",\n \"base_url\": null,\n \"implementation\": {\n \"name\": \"OPTIMADE Python Tools\",\n \"version\": \"1.1.3\",\n \"source_url\": \"https://github.com/Materials-Consortia/optimade-python-tools\",\n \"maintainer\": {\"email\": \"dev@optimade.org\"}\n },\n \"index_base_url\": null,\n \"provider\": {\n \"name\": \"Example provider\",\n \"description\": \"Provider used for examples, not to be assigned to a real database\",\n \"prefix\": \"exmpl\",\n \"homepage\": \"https://example.com\"\n },\n \"provider_fields\": {},\n \"aliases\": {},\n \"length_aliases\": {},\n \"index_links_path\": \"./optimade/server/index_links.json\",\n \"log_level\": \"info\",\n \"log_dir\": \"/var/log/optimade/\",\n \"validate_query_parameters\": true,\n \"validate_api_response\": true\n}\n
"},{"location":"api_reference/exceptions/","title":"exceptions","text":""},{"location":"api_reference/exceptions/#optimade.exceptions.BadRequest","title":"BadRequest
","text":" Bases: OptimadeHTTPException
400 Bad Request
Source code inoptimade/exceptions.py
class BadRequest(OptimadeHTTPException):\n \"\"\"400 Bad Request\"\"\"\n\n status_code: int = 400\n title: str = \"Bad Request\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.Forbidden","title":"Forbidden
","text":" Bases: OptimadeHTTPException
403 Forbidden
Source code inoptimade/exceptions.py
class Forbidden(OptimadeHTTPException):\n \"\"\"403 Forbidden\"\"\"\n\n status_code: int = 403\n title: str = \"Forbidden\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.InternalServerError","title":"InternalServerError
","text":" Bases: OptimadeHTTPException
500 Internal Server Error
Source code inoptimade/exceptions.py
class InternalServerError(OptimadeHTTPException):\n \"\"\"500 Internal Server Error\"\"\"\n\n status_code: int = 500\n title: str = \"Internal Server Error\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.NotFound","title":"NotFound
","text":" Bases: OptimadeHTTPException
404 Not Found
Source code inoptimade/exceptions.py
class NotFound(OptimadeHTTPException):\n \"\"\"404 Not Found\"\"\"\n\n status_code: int = 404\n title: str = \"Not Found\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.NotImplementedResponse","title":"NotImplementedResponse
","text":" Bases: OptimadeHTTPException
501 Not Implemented
Source code inoptimade/exceptions.py
class NotImplementedResponse(OptimadeHTTPException):\n \"\"\"501 Not Implemented\"\"\"\n\n status_code: int = 501\n title: str = \"Not Implemented\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.OptimadeHTTPException","title":"OptimadeHTTPException
","text":" Bases: Exception
, ABC
This abstract class can be subclassed to define HTTP responses with the desired status codes, and detailed error strings to represent in the JSON:API error response.
This class closely follows the starlette.HTTPException
without requiring it as a dependency, so that such errors can also be raised from within client code.
Attributes:
Name Type Descriptionstatus_code
int
The HTTP status code accompanying this exception.
title
str
A descriptive title for this exception.
detail
Optional[str]
An optional string containing the details of the error.
Source code inoptimade/exceptions.py
class OptimadeHTTPException(Exception, ABC):\n \"\"\"This abstract class can be subclassed to define\n HTTP responses with the desired status codes, and\n detailed error strings to represent in the JSON:API\n error response.\n\n This class closely follows the `starlette.HTTPException` without\n requiring it as a dependency, so that such errors can also be\n raised from within client code.\n\n Attributes:\n status_code: The HTTP status code accompanying this exception.\n title: A descriptive title for this exception.\n detail: An optional string containing the details of the error.\n\n \"\"\"\n\n status_code: int\n title: str\n detail: Optional[str] = None\n headers: Optional[dict[str, Any]] = None\n\n def __init__(\n self, detail: Optional[str] = None, headers: Optional[dict] = None\n ) -> None:\n if self.status_code is None:\n raise AttributeError(\n \"HTTPException class {self.__class__.__name__} is missing required `status_code` attribute.\"\n )\n self.detail = detail\n self.headers = headers\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else self.__repr__()\n\n def __repr__(self) -> str:\n class_name = self.__class__.__name__\n return f\"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.UnprocessableEntity","title":"UnprocessableEntity
","text":" Bases: OptimadeHTTPException
422 Unprocessable Entity
Source code inoptimade/exceptions.py
class UnprocessableEntity(OptimadeHTTPException):\n \"\"\"422 Unprocessable Entity\"\"\"\n\n status_code: int = 422\n title: str = \"Unprocessable Entity\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.VersionNotSupported","title":"VersionNotSupported
","text":" Bases: OptimadeHTTPException
553 Version Not Supported
Source code inoptimade/exceptions.py
class VersionNotSupported(OptimadeHTTPException):\n \"\"\"553 Version Not Supported\"\"\"\n\n status_code: int = 553\n title: str = \"Version Not Supported\"\n
"},{"location":"api_reference/utils/","title":"utils","text":"This submodule implements some useful utilities for dealing with OPTIMADE providers that can be used in server or client code.
"},{"location":"api_reference/utils/#optimade.utils.get_all_databases","title":"get_all_databases(include_providers=None, exclude_providers=None, exclude_databases=None, progress=None, skip_ssl=False)
","text":"Iterate through all databases reported by registered OPTIMADE providers.
Parameters:
Name Type Description Defaultinclude_providers
Optional[Container[str]]
A set/container of provider IDs to include child databases for.
None
exclude_providers
Optional[Container[str]]
A set/container of provider IDs to exclude child databases for.
None
exclude_databases
Optional[Container[str]]
A set/container of specific database URLs to exclude.
None
Returns:
Type DescriptionIterable[str]
A generator of child database links that obey the given parameters.
Source code inoptimade/utils.py
def get_all_databases(\n include_providers: Optional[Container[str]] = None,\n exclude_providers: Optional[Container[str]] = None,\n exclude_databases: Optional[Container[str]] = None,\n progress: \"Optional[rich.Progress]\" = None,\n skip_ssl: bool = False,\n) -> Iterable[str]:\n \"\"\"Iterate through all databases reported by registered OPTIMADE providers.\n\n Parameters:\n include_providers: A set/container of provider IDs to include child databases for.\n exclude_providers: A set/container of provider IDs to exclude child databases for.\n exclude_databases: A set/container of specific database URLs to exclude.\n\n Returns:\n A generator of child database links that obey the given parameters.\n\n \"\"\"\n if progress is not None:\n _task = progress.add_task(\n description=\"Retrieving all databases from registered OPTIMADE providers...\",\n total=None,\n )\n else:\n progress = contextlib.nullcontext()\n progress.print = lambda _: None # type: ignore[attr-defined]\n progress.advance = lambda *_: None # type: ignore[attr-defined]\n _task = None\n\n with progress:\n for provider in get_providers():\n if exclude_providers and provider[\"id\"] in exclude_providers:\n continue\n if include_providers and provider[\"id\"] not in include_providers:\n continue\n\n try:\n links = get_child_database_links(provider, skip_ssl=skip_ssl)\n for link in links:\n if link.attributes.base_url:\n if (\n exclude_databases\n and link.attributes.base_url in exclude_databases\n ):\n continue\n yield str(link.attributes.base_url)\n if links and progress is not None:\n progress.advance(_task, 1)\n progress.print(\n f\"Retrieved databases from [bold green]{provider['id']}[/bold green]\"\n )\n except RuntimeError as exc:\n if progress is not None:\n progress.print(\n f\"Unable to retrieve databases from [bold red]{provider['id']}[/bold red]: {exc}\",\n )\n pass\n
"},{"location":"api_reference/utils/#optimade.utils.get_child_database_links","title":"get_child_database_links(provider, obey_aggregate=True, headers=None, skip_ssl=False)
","text":"For a provider, return a list of available child databases.
Parameters:
Name Type Description Defaultprovider
LinksResource
The links entry for the provider.
requiredobey_aggregate
bool
Whether to only return links that allow aggregation.
True
headers
Optional[dict]
Additional HTTP headers to pass to the provider.
None
Returns:
Type Descriptionlist[LinksResource]
A list of the valid links entries from this provider that
list[LinksResource]
have link_type
\"child\"
.
Raises:
Type DescriptionRuntimeError
If the provider's index meta-database is down, invalid, or the request otherwise fails.
Source code inoptimade/utils.py
def get_child_database_links(\n provider: LinksResource,\n obey_aggregate: bool = True,\n headers: Optional[dict] = None,\n skip_ssl: bool = False,\n) -> list[LinksResource]:\n \"\"\"For a provider, return a list of available child databases.\n\n Arguments:\n provider: The links entry for the provider.\n obey_aggregate: Whether to only return links that allow\n aggregation.\n headers: Additional HTTP headers to pass to the provider.\n\n Returns:\n A list of the valid links entries from this provider that\n have `link_type` `\"child\"`.\n\n Raises:\n RuntimeError: If the provider's index meta-database is down,\n invalid, or the request otherwise fails.\n\n \"\"\"\n import requests\n\n from optimade.models.links import Aggregate, LinkType\n\n base_url = provider.pop(\"base_url\")\n if base_url is None:\n raise RuntimeError(f\"Provider {provider['id']} provides no base URL.\")\n\n links_endp = base_url + \"/v1/links\"\n try:\n links = requests.get(links_endp, timeout=10, headers=headers)\n except SSLError as exc:\n if skip_ssl:\n links = requests.get(links_endp, timeout=10, headers=headers, verify=False)\n else:\n raise RuntimeError(\n f\"SSL error when connecting to provider {provider['id']}. Use `skip_ssl` to ignore.\"\n ) from exc\n except (requests.ConnectionError, requests.Timeout) as exc:\n raise RuntimeError(f\"Unable to connect to provider {provider['id']}\") from exc\n\n if links.status_code != 200:\n raise RuntimeError(\n f\"Invalid response from {links_endp} for provider {provider['id']}: {links.content!r}.\"\n )\n\n try:\n links_resources = links.json().get(\"data\", [])\n return_links = []\n for link in links_resources:\n link = LinksResource(**link)\n if (\n link.attributes.link_type == LinkType.CHILD\n and link.attributes.base_url is not None\n and (not obey_aggregate or link.attributes.aggregate == Aggregate.OK)\n ):\n return_links.append(link)\n\n return return_links\n\n except (ValidationError, json.JSONDecodeError) as exc:\n raise RuntimeError(\n f\"Did not understand response from {provider['id']}: {links.content!r}, {exc}\"\n )\n
"},{"location":"api_reference/utils/#optimade.utils.get_providers","title":"get_providers(add_mongo_id=False)
","text":"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).
Fallback order if providers.optimade.org is not available:
providers
' list of providers./links
-endpoint.Parameters:
Name Type Description Defaultadd_mongo_id
bool
Whether to populate the _id
field of the provider with MongoDB ObjectID.
False
Returns:
Type Descriptionlist
List of raw JSON-decoded providers including MongoDB object IDs.
Source code inoptimade/utils.py
def get_providers(add_mongo_id: bool = False) -> list:\n \"\"\"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).\n\n Fallback order if providers.optimade.org is not available:\n\n 1. Try Materials-Consortia/providers on GitHub.\n 2. Try submodule `providers`' list of providers.\n 3. Log warning that providers list from Materials-Consortia is not included in the\n `/links`-endpoint.\n\n Arguments:\n add_mongo_id: Whether to populate the `_id` field of the provider with MongoDB\n ObjectID.\n\n Returns:\n List of raw JSON-decoded providers including MongoDB object IDs.\n\n \"\"\"\n import json\n\n import requests\n\n for provider_list_url in PROVIDER_LIST_URLS:\n try:\n providers = requests.get(provider_list_url, timeout=10).json()\n except (\n requests.exceptions.ConnectionError,\n requests.exceptions.ConnectTimeout,\n json.JSONDecodeError,\n requests.exceptions.SSLError,\n ):\n pass\n else:\n break\n else:\n try:\n from optimade.server.data import providers # type: ignore\n except ImportError:\n from optimade.server.logger import LOGGER\n\n LOGGER.warning(\n \"\"\"Could not retrieve a list of providers!\n\n Tried the following resources:\n\n{}\n The list of providers will not be included in the `/links`-endpoint.\n\"\"\".format(\"\".join([f\" * {_}\\n\" for _ in PROVIDER_LIST_URLS]))\n )\n return []\n\n providers_list = []\n for provider in providers.get(\"data\", []):\n # Remove/skip \"exmpl\"\n if provider[\"id\"] == \"exmpl\":\n continue\n\n provider.update(provider.pop(\"attributes\", {}))\n\n # Add MongoDB ObjectId\n if add_mongo_id:\n provider[\"_id\"] = {\n \"$oid\": mongo_id_for_database(provider[\"id\"], provider[\"type\"])\n }\n\n providers_list.append(provider)\n\n return providers_list\n
"},{"location":"api_reference/utils/#optimade.utils.insert_from_jsonl","title":"insert_from_jsonl(jsonl_path)
","text":"Insert OPTIMADE JSON lines data into the database.
Parameters:
Name Type Description Defaultjsonl_path
Path
Path to the JSON lines file.
required Source code inoptimade/utils.py
def insert_from_jsonl(jsonl_path: Path) -> None:\n \"\"\"Insert OPTIMADE JSON lines data into the database.\n\n Arguments:\n jsonl_path: Path to the JSON lines file.\n\n \"\"\"\n from collections import defaultdict\n\n import bson.json_util\n\n from optimade.server.routers import ENTRY_COLLECTIONS\n\n batch = defaultdict(list)\n batch_size: int = 1000\n\n # Attempt to treat path as absolute, otherwise join with root directory\n if not jsonl_path.is_file():\n _jsonl_path = Path(__file__).parent.joinpath(jsonl_path)\n if not _jsonl_path.is_file():\n raise FileNotFoundError(\n f\"Could not find file {jsonl_path} or {_jsonl_path}\"\n )\n jsonl_path = _jsonl_path\n\n with open(jsonl_path) as handle:\n header = handle.readline()\n header_jsonl = json.loads(header)\n assert header_jsonl.get(\n \"x-optimade\"\n ), \"No x-optimade header, not sure if this is a JSONL file\"\n\n for json_str in handle:\n try:\n entry = bson.json_util.loads(json_str)\n except json.JSONDecodeError:\n print(f\"Could not read entry as JSON: {json_str}\")\n print_exc()\n continue\n try:\n id = entry.get(\"id\", None)\n _type = entry.get(\"type\", None)\n if id is None or _type == \"info\":\n # assume this is an info endpoint for pre-1.2\n continue\n\n inp_data = entry[\"attributes\"]\n inp_data[\"id\"] = id\n # Append the data to the batch\n batch[_type].append(inp_data)\n except Exception as exc:\n print(f\"Error with entry {entry}: {exc}\")\n print_exc()\n continue\n\n if len(batch[_type]) >= batch_size:\n ENTRY_COLLECTIONS[_type].insert(batch[_type])\n batch[_type] = []\n\n # Insert any remaining data\n for entry_type in batch:\n ENTRY_COLLECTIONS[entry_type].insert(batch[entry_type])\n batch[entry_type] = []\n
"},{"location":"api_reference/utils/#optimade.utils.mongo_id_for_database","title":"mongo_id_for_database(database_id, database_type)
","text":"Produce a MongoDB ObjectId for a database
Source code inoptimade/utils.py
def mongo_id_for_database(database_id: str, database_type: str) -> str:\n \"\"\"Produce a MongoDB ObjectId for a database\"\"\"\n from bson.objectid import ObjectId\n\n oid = f\"{database_id}{database_type}\"\n if len(oid) > 12:\n oid = oid[:12]\n elif len(oid) < 12:\n oid = f\"{oid}{'0' * (12 - len(oid))}\"\n\n return str(ObjectId(oid.encode(\"UTF-8\")))\n
"},{"location":"api_reference/warnings/","title":"warnings","text":"This submodule implements the possible warnings that can be omitted by an OPTIMADE API.
"},{"location":"api_reference/warnings/#optimade.warnings.FieldValueNotRecognized","title":"FieldValueNotRecognized
","text":" Bases: OptimadeWarning
A field or value used in the request is not recognised by this implementation.
Source code inoptimade/warnings.py
class FieldValueNotRecognized(OptimadeWarning):\n \"\"\"A field or value used in the request is not recognised by this implementation.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.LocalOptimadeWarning","title":"LocalOptimadeWarning
","text":" Bases: OptimadeWarning
A warning that is specific to a local implementation of the OPTIMADE API and should not appear in the server log or response.
Source code inoptimade/warnings.py
class LocalOptimadeWarning(OptimadeWarning):\n \"\"\"A warning that is specific to a local implementation of the OPTIMADE API\n and should not appear in the server log or response.\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.MissingExpectedField","title":"MissingExpectedField
","text":" Bases: LocalOptimadeWarning
A field was provided with a null value when a related field was provided with a value.
Source code inoptimade/warnings.py
class MissingExpectedField(LocalOptimadeWarning):\n \"\"\"A field was provided with a null value when a related field was provided\n with a value.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.OptimadeWarning","title":"OptimadeWarning
","text":" Bases: Warning
Base Warning for the optimade
package
optimade/warnings.py
class OptimadeWarning(Warning):\n \"\"\"Base Warning for the `optimade` package\"\"\"\n\n def __init__(\n self, detail: Optional[str] = None, title: Optional[str] = None, *args\n ) -> None:\n detail = detail if detail else self.__doc__\n super().__init__(detail, *args)\n self.detail = detail\n self.title = title if title else self.__class__.__name__\n\n def __repr__(self) -> str:\n attrs = {\"detail\": self.detail, \"title\": self.title}\n return \"<{:s}({:s})>\".format(\n self.__class__.__name__,\n \" \".join(\n [\n f\"{attr}={value!r}\"\n for attr, value in attrs.items()\n if value is not None\n ]\n ),\n )\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else \"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.QueryParamNotUsed","title":"QueryParamNotUsed
","text":" Bases: OptimadeWarning
A query parameter is not used in this request.
Source code inoptimade/warnings.py
class QueryParamNotUsed(OptimadeWarning):\n \"\"\"A query parameter is not used in this request.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.TimestampNotRFCCompliant","title":"TimestampNotRFCCompliant
","text":" Bases: OptimadeWarning
A timestamp has been used in a filter that contains microseconds and is thus not RFC 3339 compliant. This may cause undefined behaviour in the query results.
Source code inoptimade/warnings.py
class TimestampNotRFCCompliant(OptimadeWarning):\n \"\"\"A timestamp has been used in a filter that contains microseconds and is thus not\n RFC 3339 compliant. This may cause undefined behaviour in the query results.\n\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.TooManyValues","title":"TooManyValues
","text":" Bases: OptimadeWarning
A field or query parameter has too many values to be handled by this implementation.
Source code inoptimade/warnings.py
class TooManyValues(OptimadeWarning):\n \"\"\"A field or query parameter has too many values to be handled by this implementation.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.UnknownProviderProperty","title":"UnknownProviderProperty
","text":" Bases: OptimadeWarning
A provider-specific property has been requested via response_fields
or as in a filter
that is not recognised by this implementation.
optimade/warnings.py
class UnknownProviderProperty(OptimadeWarning):\n \"\"\"A provider-specific property has been requested via `response_fields` or as in a `filter` that is not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.UnknownProviderQueryParameter","title":"UnknownProviderQueryParameter
","text":" Bases: OptimadeWarning
A provider-specific query parameter has been requested in the query with a prefix not recognised by this implementation.
Source code inoptimade/warnings.py
class UnknownProviderQueryParameter(OptimadeWarning):\n \"\"\"A provider-specific query parameter has been requested in the query with a prefix not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/adapters/base/","title":"base","text":"The base for all adapters.
An entry resource adapter is a tool to wrap OPTIMADE JSON-deserialized Python dictionaries in the relevant pydantic model for the particular resource.
This means data resources in an OPTIMADE REST API response can be converted to valid Python types written specifically for them. One can then use the standard pydantic functionality on the wrapped objects, reasoning about the embedded hierarchical types as well as retrieve default values for properties not supplied by the raw API response resource.
Furthermore, the entry resource adapter allows conversion between the entry resource and any implemented equivalent data structure.
See Reference
and Structure
to find out what the entry resources can be converted to for ReferenceResource
s and StructureResource
s, respectively.
EntryAdapter
","text":"Base class for lazy resource entry adapters.
Attributes:
Name Type DescriptionENTRY_RESOURCE
type[EntryResource]
Entry resource to store entry as.
_type_converters
dict[str, Callable]
Dictionary of valid conversion types for entry.
_type_ingesters
dict[str, Callable]
Dictionary of valid ingestion types mapped to ingestion functions.
_type_ingesters_by_type
dict[str, type]
Dictionary mapping the keys of _type_ingesters
to data types that can be ingested.
as_<_type_converters>
dict[str, type]
Convert entry to a type listed in _type_converters
.
from_<_type_converters>
dict[str, type]
Convert an external type to the corresponding OPTIMADE model.
Source code inoptimade/adapters/base.py
class EntryAdapter:\n \"\"\"\n Base class for lazy resource entry adapters.\n\n Attributes:\n ENTRY_RESOURCE: Entry resource to store entry as.\n _type_converters: Dictionary of valid conversion types for entry.\n _type_ingesters: Dictionary of valid ingestion types mapped to ingestion functions.\n _type_ingesters_by_type: Dictionary mapping the keys of `_type_ingesters` to data\n types that can be ingested.\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n from_<_type_converters>: Convert an external type to the corresponding OPTIMADE model.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[EntryResource] = EntryResource\n _type_converters: dict[str, Callable] = {}\n _type_ingesters: dict[str, Callable] = {}\n _type_ingesters_by_type: dict[str, type] = {}\n\n def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n\n @property\n def entry(self) -> EntryResource:\n \"\"\"Get OPTIMADE entry.\n\n Returns:\n The entry resource.\n\n \"\"\"\n return self._entry\n\n def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n\n @classmethod\n def ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n\n @classmethod\n def from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n\n @staticmethod\n def _get_model_attributes(\n starting_instances: Union[tuple[BaseModel, ...], list[BaseModel]], name: str\n ) -> Any:\n \"\"\"Helper method for retrieving the OPTIMADE model's attribute, supporting \".\"-nested attributes\"\"\"\n for res in starting_instances:\n nested_attributes = name.split(\".\")\n for nested_attribute in nested_attributes:\n if nested_attribute in getattr(res, \"model_fields\", {}):\n res = getattr(res, nested_attribute)\n else:\n res = None\n break\n if res is not None:\n return res\n raise AttributeError\n\n def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/exceptions/","title":"exceptions","text":""},{"location":"api_reference/adapters/exceptions/#optimade.adapters.exceptions.ConversionError","title":"ConversionError
","text":" Bases: Exception
Could not convert entry to format
Source code inoptimade/adapters/exceptions.py
class ConversionError(Exception):\n \"\"\"Could not convert entry to format\"\"\"\n
"},{"location":"api_reference/adapters/logger/","title":"logger","text":"Logger for optimade.adapters
"},{"location":"api_reference/adapters/warnings/","title":"warnings","text":""},{"location":"api_reference/adapters/warnings/#optimade.adapters.warnings.AdapterPackageNotFound","title":"AdapterPackageNotFound
","text":" Bases: OptimadeWarning
The package for an adapter cannot be found.
Source code inoptimade/adapters/warnings.py
class AdapterPackageNotFound(OptimadeWarning):\n \"\"\"The package for an adapter cannot be found.\"\"\"\n
"},{"location":"api_reference/adapters/warnings/#optimade.adapters.warnings.ConversionWarning","title":"ConversionWarning
","text":" Bases: OptimadeWarning
A non-critical error/fallback/choice happened during conversion of an entry to format.
Source code inoptimade/adapters/warnings.py
class ConversionWarning(OptimadeWarning):\n \"\"\"A non-critical error/fallback/choice happened during conversion of an entry to format.\"\"\"\n
"},{"location":"api_reference/adapters/references/adapter/","title":"adapter","text":""},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference","title":"Reference
","text":" Bases: EntryAdapter
Lazy reference resource converter.
Go to EntryAdapter
to see the full list of methods and properties.
Attributes:
Name Type DescriptionENTRY_RESOURCE
ReferenceResource
This adapter stores entry resources as ReferenceResource
s.
_type_converters
Dict[str, Callable]
Dictionary of valid conversion types for entry.
There are currently no available types.
as_<_type_converters>
Dict[str, Callable]
Convert entry to a type listed in _type_converters
.
optimade/adapters/references/adapter.py
class Reference(EntryAdapter):\n \"\"\"\n Lazy reference resource converter.\n\n Go to [`EntryAdapter`][optimade.adapters.base.EntryAdapter] to see the full list of methods\n and properties.\n\n Attributes:\n ENTRY_RESOURCE (ReferenceResource): This adapter stores entry resources as\n [`ReferenceResource`][optimade.models.references.ReferenceResource]s.\n _type_converters (Dict[str, Callable]): Dictionary of valid conversion types for entry.\n\n There are currently no available types.\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[ReferenceResource] = ReferenceResource\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/structures/adapter/","title":"adapter","text":""},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure","title":"Structure
","text":" Bases: EntryAdapter
Lazy structure resource converter.
Go to EntryAdapter
to see the full list of methods and properties.
Attributes:
Name Type DescriptionENTRY_RESOURCE
type[StructureResource]
This adapter stores entry resources as StructureResource
s.
_type_converters
dict[str, Callable]
Dictionary of valid conversion types for entry.
Currently available types:
aiida_structuredata
ase
cif
pdb
pdbx_mmcif
pymatgen
jarvis
_type_ingesters
dict[str, Callable]
Dictionary of valid ingesters.
as_<_type_converters>
dict[str, Callable]
Convert entry to a type listed in _type_converters
.
from_<_type_converters>
dict[str, Callable]
Convert an external type to an OPTIMADE StructureResourceAttributes
model.
optimade/adapters/structures/adapter.py
class Structure(EntryAdapter):\n \"\"\"\n Lazy structure resource converter.\n\n Go to [`EntryAdapter`][optimade.adapters.base.EntryAdapter] to see the full list of methods\n and properties.\n\n Attributes:\n ENTRY_RESOURCE: This adapter stores entry resources as\n [`StructureResource`][optimade.models.structures.StructureResource]s.\n _type_converters: Dictionary of valid conversion types for entry.\n\n Currently available types:\n\n - `aiida_structuredata`\n - `ase`\n - `cif`\n - `pdb`\n - `pdbx_mmcif`\n - `pymatgen`\n - `jarvis`\n\n _type_ingesters: Dictionary of valid ingesters.\n\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n from_<_type_converters>: Convert an external type to an OPTIMADE\n [`StructureResourceAttributes`][optimade.models.structures.StructureResourceAttributes]\n model.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[StructureResource] = StructureResource\n _type_converters: dict[str, Callable] = {\n \"aiida_structuredata\": get_aiida_structure_data,\n \"ase\": get_ase_atoms,\n \"cif\": get_cif,\n \"pdb\": get_pdb,\n \"pdbx_mmcif\": get_pdbx_mmcif,\n \"pymatgen\": get_pymatgen,\n \"jarvis\": get_jarvis_atoms,\n }\n\n _type_ingesters: dict[str, Callable] = {\n \"pymatgen\": from_pymatgen,\n \"ase\": from_ase_atoms,\n }\n\n _type_ingesters_by_type: dict[str, type] = {\n \"pymatgen\": PymatgenStructure,\n \"ase\": ASEAtoms,\n }\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/structures/aiida/","title":"aiida","text":"Convert an OPTIMADE structure, in the format of StructureResource
to an AiiDA StructureData
Node.
For more information on the AiiDA code see their website.
This conversion function relies on the aiida-core
package.
get_aiida_structure_data(optimade_structure)
","text":"Get AiiDA StructureData
from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionStructureData
AiiDA StructureData
Node.
optimade/adapters/structures/aiida.py
def get_aiida_structure_data(optimade_structure: OptimadeStructure) -> StructureData:\n \"\"\"Get AiiDA `StructureData` from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n AiiDA `StructureData` Node.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"StructureData\")):\n warn(AIIDA_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Convert null/None values to float(\"nan\")\n lattice_vectors, adjust_cell = pad_cell(attributes.lattice_vectors) # type: ignore[arg-type]\n structure = StructureData(cell=lattice_vectors)\n\n # If species not provided, infer data from species_at_sites\n species: Optional[list[OptimadeStructureSpecies]] = attributes.species\n if not species:\n species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type]\n\n # Add Kinds\n for kind in species:\n symbols = []\n concentration = []\n mass = 0.0\n for index, chemical_symbol in enumerate(kind.chemical_symbols):\n # NOTE: The non-chemical element identifier \"X\" is identical to how AiiDA handles this,\n # so it will be treated the same as any other true chemical identifier.\n if chemical_symbol == \"vacancy\":\n # Skip. This is how AiiDA handles vacancies;\n # to not include them, while keeping the concentration in a site less than 1.\n continue\n else:\n symbols.append(chemical_symbol)\n concentration.append(kind.concentration[index])\n\n # AiiDA needs a definition for the mass, and for it to be > 0\n # mass is OPTIONAL for OPTIMADE structures\n if kind.mass:\n mass += kind.concentration[index] * kind.mass[index]\n\n if not mass:\n warn(\n f\"No mass defined for <species(name={kind.name!r})>, will default to setting mass to 1.0.\",\n ConversionWarning,\n )\n\n structure.append_kind(\n Kind(\n symbols=symbols, weights=concentration, mass=mass or 1.0, name=kind.name\n )\n )\n\n # Add Sites\n for index in range(attributes.nsites): # type: ignore[arg-type]\n # range() to ensure 1-to-1 between kind and site\n structure.append_site(\n Site(\n kind_name=attributes.species_at_sites[index], # type: ignore[index]\n position=attributes.cartesian_site_positions[index], # type: ignore[index]\n )\n )\n\n if adjust_cell:\n structure._adjust_default_cell(\n pbc=[bool(dim.value) for dim in attributes.dimension_types] # type: ignore[union-attr]\n )\n\n return structure\n
"},{"location":"api_reference/adapters/structures/ase/","title":"ase","text":"Convert an OPTIMADE structure, in the format of StructureResource
to an ASE Atoms
object.
This conversion function relies on the ASE code.
For more information on the ASE code see their documentation.
"},{"location":"api_reference/adapters/structures/ase/#optimade.adapters.structures.ase.from_ase_atoms","title":"from_ase_atoms(atoms)
","text":"Convert an ASE Atoms
object into an OPTIMADE StructureResourceAttributes
model.
Parameters:
Name Type Description Defaultatoms
Atoms
The ASE Atoms
object to convert.
Returns:
Type DescriptionStructureResourceAttributes
An OPTIMADE StructureResourceAttributes
model, which can be converted to a raw Python dictionary with .model_dump()
or to JSON with .model_dump_json()
.
optimade/adapters/structures/ase.py
def from_ase_atoms(atoms: Atoms) -> StructureResourceAttributes:\n \"\"\"Convert an ASE `Atoms` object into an OPTIMADE `StructureResourceAttributes` model.\n\n Parameters:\n atoms: The ASE `Atoms` object to convert.\n\n Returns:\n An OPTIMADE `StructureResourceAttributes` model, which can be converted to a raw Python\n dictionary with `.model_dump()` or to JSON with `.model_dump_json()`.\n\n \"\"\"\n if not isinstance(atoms, Atoms):\n raise RuntimeError(\n f\"Cannot convert type {type(atoms)} into an OPTIMADE `StructureResourceAttributes` model.\"\n )\n\n attributes = {}\n attributes[\"cartesian_site_positions\"] = atoms.positions.tolist()\n attributes[\"lattice_vectors\"] = atoms.cell.tolist()\n attributes[\"species_at_sites\"] = atoms.get_chemical_symbols()\n attributes[\"elements_ratios\"] = elements_ratios_from_species_at_sites(\n attributes[\"species_at_sites\"]\n )\n attributes[\"species\"] = species_from_species_at_sites(\n attributes[\"species_at_sites\"]\n )\n attributes[\"dimension_types\"] = [int(_) for _ in atoms.pbc.tolist()]\n attributes[\"nperiodic_dimensions\"] = sum(attributes[\"dimension_types\"])\n attributes[\"nelements\"] = len(attributes[\"species\"])\n attributes[\"elements\"] = sorted([_.name for _ in attributes[\"species\"]])\n attributes[\"nsites\"] = len(attributes[\"species_at_sites\"])\n\n attributes[\"chemical_formula_descriptive\"] = atoms.get_chemical_formula()\n attributes[\"chemical_formula_reduced\"] = reduce_formula(\n atoms.get_chemical_formula()\n )\n attributes[\"chemical_formula_anonymous\"] = anonymize_formula(\n attributes[\"chemical_formula_reduced\"],\n )\n attributes[\"last_modified\"] = None\n attributes[\"immutable_id\"] = None\n attributes[\"structure_features\"] = []\n\n for key in atoms.info:\n optimade_key = key.lower()\n if not key.startswith(f\"_{EXTRA_FIELD_PREFIX}\"):\n optimade_key = f\"_{EXTRA_FIELD_PREFIX}_{optimade_key}\"\n attributes[optimade_key] = atoms.info[key]\n\n return StructureResourceAttributes(**attributes)\n
"},{"location":"api_reference/adapters/structures/ase/#optimade.adapters.structures.ase.get_ase_atoms","title":"get_ase_atoms(optimade_structure)
","text":"Get ASE Atoms
from OPTIMADE structure.
Cannot handle partial occupancies (this includes vacancies).
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionAtoms
ASE Atoms
object.
optimade/adapters/structures/ase.py
def get_ase_atoms(optimade_structure: OptimadeStructure) -> Atoms:\n \"\"\"Get ASE `Atoms` from OPTIMADE structure.\n\n Caution:\n Cannot handle partial occupancies (this includes vacancies).\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n ASE `Atoms` object.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Atoms\")):\n warn(ASE_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Cannot handle partial occupancies\n if StructureFeatures.DISORDER in attributes.structure_features:\n raise ConversionError(\n \"ASE cannot handle structures with partial occupancies, sorry.\"\n )\n\n species = attributes.species\n # If species is missing, infer data from species_at_sites\n if not species:\n species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type]\n\n optimade_species: dict[str, OptimadeStructureSpecies] = {_.name: _ for _ in species}\n\n # Since we've made sure there are no species with more than 1 chemical symbol,\n # asking for index 0 will always work.\n if \"X\" in [specie.chemical_symbols[0] for specie in optimade_species.values()]:\n raise ConversionError(\n \"ASE cannot handle structures with unknown ('X') chemical symbols, sorry.\"\n )\n\n atoms = []\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = attributes.cartesian_site_positions[site_number] # type: ignore[index]\n\n current_species = optimade_species[species_name]\n\n # Argument above about chemical symbols also holds here\n mass = None\n if current_species.mass:\n mass = current_species.mass[0]\n\n atoms.append(\n Atom(symbol=current_species.chemical_symbols[0], position=site, mass=mass)\n )\n\n info = {}\n for key in attributes.model_dump().keys():\n if key.startswith(\"_\"):\n ase_key = key\n if key.startswith(f\"_{EXTRA_FIELD_PREFIX}_\"):\n ase_key = \"\".join(key.split(f\"_{EXTRA_FIELD_PREFIX}_\")[1:])\n info[ase_key] = getattr(attributes, key)\n\n return Atoms(\n symbols=atoms,\n cell=attributes.lattice_vectors,\n pbc=attributes.dimension_types,\n info=info if info else None,\n )\n
"},{"location":"api_reference/adapters/structures/cif/","title":"cif","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a CIF file (Crystallographic Information File).
For more information on the CIF file format, see the official documentation.
NoteThis conversion function is inspired heavily by the similar conversion function in the ASE library.
See here for the original ASE code.
For more information on the ASE library, see their documentation.
This conversion function relies on the NumPy library.
"},{"location":"api_reference/adapters/structures/cif/#optimade.adapters.structures.cif.get_cif","title":"get_cif(optimade_structure)
","text":"Get CIF file as string from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type Descriptionstr
The CIF file as a single Python str
object.
optimade/adapters/structures/cif.py
def get_cif(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Get CIF file as string from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n The CIF file as a single Python `str` object.\n\n \"\"\"\n # NumPy is needed for calculations\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cif = \"\"\"#\n# Created from an OPTIMADE structure.\n#\n# See https://www.optimade.org and/or\n# https://github.com/Materials-Consortia/OPTIMADE for more information.\n#\n\"\"\"\n\n cif += f\"data_{optimade_structure.id}\\n\\n\"\n\n attributes = optimade_structure.attributes\n\n # Do this only if there's three non-zero lattice vectors\n # NOTE: This also negates handling of lattice_vectors with null/None values\n if valid_lattice_vector(attributes.lattice_vectors): # type:ignore[arg-type]\n a_vector, b_vector, c_vector, alpha, beta, gamma = cell_to_cellpar(\n attributes.lattice_vectors # type: ignore[arg-type]\n )\n\n cif += (\n f\"_cell_length_a {a_vector:g}\\n\"\n f\"_cell_length_b {b_vector:g}\\n\"\n f\"_cell_length_c {c_vector:g}\\n\"\n f\"_cell_angle_alpha {alpha:g}\\n\"\n f\"_cell_angle_beta {beta:g}\\n\"\n f\"_cell_angle_gamma {gamma:g}\\n\\n\"\n )\n cif += (\n \"_symmetry_space_group_name_H-M 'P 1'\\n\"\n \"_symmetry_int_tables_number 1\\n\\n\"\n \"loop_\\n\"\n \" _symmetry_equiv_pos_as_xyz\\n\"\n \" 'x, y, z'\\n\\n\"\n )\n\n # Since some structure viewers are having issues with cartesian coordinates,\n # we calculate the fractional coordinates if this is a 3D structure and we have all the necessary information.\n if not hasattr(attributes, \"fractional_site_positions\"):\n attributes.fractional_site_positions = fractional_coordinates(\n cell=attributes.lattice_vectors, # type:ignore[arg-type]\n cartesian_positions=attributes.cartesian_site_positions, # type:ignore[arg-type]\n )\n\n # NOTE: This is otherwise a bit ahead of its time, since this OPTIMADE property is part of an open PR.\n # See https://github.com/Materials-Consortia/OPTIMADE/pull/206\n coord_type = (\n \"fract\" if hasattr(attributes, \"fractional_site_positions\") else \"Cartn\"\n )\n\n cif += (\n \"loop_\\n\"\n \" _atom_site_type_symbol\\n\" # species.chemical_symbols\n \" _atom_site_label\\n\" # species.name + unique int\n \" _atom_site_occupancy\\n\" # species.concentration\n f\" _atom_site_{coord_type}_x\\n\" # cartesian_site_positions\n f\" _atom_site_{coord_type}_y\\n\" # cartesian_site_positions\n f\" _atom_site_{coord_type}_z\\n\" # cartesian_site_positions\n \" _atom_site_thermal_displace_type\\n\" # Set to 'Biso'\n \" _atom_site_B_iso_or_equiv\\n\" # Set to 1.0:f\n )\n\n if coord_type == \"fract\":\n sites = attributes.fractional_site_positions\n else:\n sites = attributes.cartesian_site_positions\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type: ignore[union-attr]\n }\n\n symbol_occurences: dict[str, int] = {}\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n if symbol in symbol_occurences:\n symbol_occurences[symbol] += 1\n else:\n symbol_occurences[symbol] = 1\n label = f\"{symbol}{symbol_occurences[symbol]}\"\n\n cif += (\n f\" {symbol} {label} {current_species.concentration[index]:6.4f} {site[0]:8.5f} \"\n f\"{site[1]:8.5f} {site[2]:8.5f} {'Biso':4} {'1.000':6}\\n\"\n )\n\n return cif\n
"},{"location":"api_reference/adapters/structures/jarvis/","title":"jarvis","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a JARVIS Atoms
object.
For more information on the NIST-JARVIS repository, see their website.
This conversion function relies on the jarvis-tools package.
Contributing author
This conversion function was contributed by Kamal Choudhary (@knc6).
"},{"location":"api_reference/adapters/structures/jarvis/#optimade.adapters.structures.jarvis.get_jarvis_atoms","title":"get_jarvis_atoms(optimade_structure)
","text":"Get jarvis Atoms
from OPTIMADE structure.
Cannot handle partial occupancies.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionAtoms
A jarvis Atoms
object.
optimade/adapters/structures/jarvis.py
def get_jarvis_atoms(optimade_structure: OptimadeStructure) -> Atoms:\n \"\"\"Get jarvis `Atoms` from OPTIMADE structure.\n\n Caution:\n Cannot handle partial occupancies.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A jarvis `Atoms` object.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Atoms\")):\n warn(JARVIS_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Cannot handle partial occupancies\n if StructureFeatures.DISORDER in attributes.structure_features:\n raise ConversionError(\n \"jarvis-tools cannot handle structures with partial occupancies.\"\n )\n\n return Atoms(\n lattice_mat=attributes.lattice_vectors,\n elements=[specie.name for specie in attributes.species], # type: ignore[union-attr]\n coords=attributes.cartesian_site_positions,\n cartesian=True,\n )\n
"},{"location":"api_reference/adapters/structures/proteindatabank/","title":"proteindatabank","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a PDB file or PDBx/mmCIF file (Protein Data Bank).
For more information on the file formats, see this FAQ page from the wwPDB website.
NoteThese conversion functions are inspired heavily by the similar conversion functions in the ASE library.
See here (PDB) and here (PDBx/mmCIF) for the original ASE code.
For more information on the ASE library, see their documentation.
These conversion functions both rely on the NumPy library.
WarningCurrently, the PDBx/mmCIF conversion function is not parsing as a complete PDBx/mmCIF file.
"},{"location":"api_reference/adapters/structures/proteindatabank/#optimade.adapters.structures.proteindatabank.get_pdb","title":"get_pdb(optimade_structure)
","text":"Write Protein Data Bank (PDB) structure in the old PDB format from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type Descriptionstr
A PDB file as a single Python str
object.
optimade/adapters/structures/proteindatabank.py
def get_pdb(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Write Protein Data Bank (PDB) structure in the old PDB format from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A PDB file as a single Python `str` object.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n pdb = \"\"\n\n attributes = optimade_structure.attributes\n\n rotation = None\n if valid_lattice_vector(attributes.lattice_vectors): # type: ignore[arg-type]\n currentcell = np.asarray(attributes.lattice_vectors)\n cellpar = cell_to_cellpar(currentcell)\n exportedcell = cellpar_to_cell(cellpar)\n rotation = np.linalg.solve(currentcell, exportedcell)\n # Setting Z-value = 1 and using P1 since we have all atoms defined explicitly\n Z = 1\n spacegroup = \"P 1\"\n pdb += (\n f\"CRYST1{cellpar[0]:9.3f}{cellpar[1]:9.3f}{cellpar[2]:8.3f}\"\n f\"{cellpar[3]:7.2f}{cellpar[4]:7.2f}{cellpar[5]:7.2f} {spacegroup:11s}{Z:4d}\\n\"\n )\n\n for i, vector in enumerate(scaled_cell(currentcell)):\n pdb += f\"SCALE{i + 1} {vector[0]:10.6f}{vector[1]:10.6f}{vector[2]:10.6f} {0:10.5f}\\n\"\n\n # There is a limit of 5 digit numbers in this field.\n pdb_maxnum = 100000\n bfactor = 1.0\n\n pdb += \"MODEL 1\\n\"\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type:ignore[union-attr]\n }\n\n sites = np.asarray(attributes.cartesian_site_positions)\n if rotation is not None:\n sites = sites.dot(rotation)\n\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n label = species_name\n if len(current_species.chemical_symbols) > 1:\n if (\n \"vacancy\" in current_species.chemical_symbols\n and len(current_species.chemical_symbols) == 2\n ):\n pass\n else:\n label = f\"{symbol}{index + 1}\"\n\n pdb += (\n f\"ATOM {site_number % pdb_maxnum:5d} {label:4} MOL 1 \"\n f\"{site[0]:8.3f}{site[1]:8.3f}{site[2]:8.3f}\"\n f\"{current_species.concentration[index]:6.2f}\"\n f\"{bfactor:6.2f} {symbol.upper():2} \\n\"\n )\n pdb += \"ENDMDL\\n\"\n\n return pdb\n
"},{"location":"api_reference/adapters/structures/proteindatabank/#optimade.adapters.structures.proteindatabank.get_pdbx_mmcif","title":"get_pdbx_mmcif(optimade_structure)
","text":"Write Protein Data Bank (PDB) structure in the PDBx/mmCIF format from OPTIMADE structure.
WarningThe result of this function can currently not be parsed as a complete PDBx/mmCIF file.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
required ReturnA modern PDBx/mmCIF file as a single Python str
object.
optimade/adapters/structures/proteindatabank.py
def get_pdbx_mmcif(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Write Protein Data Bank (PDB) structure in the PDBx/mmCIF format from OPTIMADE structure.\n\n Warning:\n The result of this function can currently not be parsed as a complete PDBx/mmCIF file.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Return:\n A modern PDBx/mmCIF file as a single Python `str` object.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cif = \"\"\"#\n# Created from an OPTIMADE structure.\n#\n# See https://www.optimade.org and/or\n# https://github.com/Materials-Consortia/OPTIMADE for more information.\n#\n# CIF 2.0 format, specifically mmCIF (PDBx).\n# See http://mmcif.wwpdb.org for more information.\n#\n\"\"\"\n\n entry_id = f\"{optimade_structure.type}{optimade_structure.id}\"\n cif += f\"data_{entry_id}\\n_entry.id {entry_id}\\n#\\n\"\n\n attributes = optimade_structure.attributes\n\n # Do this only if there's three non-zero lattice vectors\n if valid_lattice_vector(attributes.lattice_vectors): # type: ignore[arg-type]\n a_vector, b_vector, c_vector, alpha, beta, gamma = cell_to_cellpar(\n attributes.lattice_vectors # type: ignore[arg-type]\n )\n\n cif += (\n f\"_cell.entry_id {entry_id}\\n\"\n f\"_cell.length_a {a_vector:g}\\n\"\n f\"_cell.length_b {b_vector:g}\\n\"\n f\"_cell.length_c {c_vector:g}\\n\"\n f\"_cell.angle_alpha {alpha:g}\\n\"\n f\"_cell.angle_beta {beta:g}\\n\"\n f\"_cell.angle_gamma {gamma:g}\\n\"\n \"_cell.Z_PDB 1\\n#\\n\"\n )\n cif += (\n f\"_symmetry.entry_id {entry_id}\\n\"\n \"_symmetry.space_group_name_H-M 'P 1'\\n\"\n \"_symmetry.Int_Tables_number 1\\n#\\n\"\n )\n\n # Since some structure viewers are having issues with cartesian coordinates,\n # we calculate the fractional coordinates if this is a 3D structure and we have all the necessary information.\n if not hasattr(attributes, \"fractional_site_positions\"):\n attributes.fractional_site_positions = fractional_coordinates(\n cell=attributes.lattice_vectors, # type: ignore[arg-type]\n cartesian_positions=attributes.cartesian_site_positions, # type: ignore[arg-type]\n )\n\n # NOTE: The following lines are perhaps needed to create a \"valid\" PDBx/mmCIF file.\n # However, at the same time, the information here is \"default\" and will for all structures \"at this moment in time\"\n # be the same. I.e., no information is gained by adding this now.\n # If it is found that they indeed are needed to create a \"valid\" PDBx/mmCIF file, they should be included in the output.\n # cif += (\n # \"loop_\\n\"\n # \"_struct_asym.id\\n\"\n # \"_struct_asym.entity_id\\n\"\n # \"A 1\\n#\\n\" # At this point, not using this feature.\n # )\n\n # cif += (\n # \"loop_\\n\"\n # \"_chem_comp.id\\n\"\n # \"X\\n#\\n\" # At this point, not using this feature.\n # )\n\n # cif += (\n # \"loop_\\n\"\n # \"_entity.id\\n\"\n # \"1\\n#\\n\" # At this point, not using this feature.\n # )\n\n # NOTE: This is otherwise a bit ahead of its time, since this OPTIMADE property is part of an open PR.\n # See https://github.com/Materials-Consortia/OPTIMADE/pull/206\n coord_type = (\n \"fract\" if hasattr(attributes, \"fractional_site_positions\") else \"Cartn\"\n )\n\n cif += (\n \"loop_\\n\"\n \"_atom_site.group_PDB\\n\" # Always \"ATOM\"\n \"_atom_site.id\\n\" # number (1-counting)\n \"_atom_site.type_symbol\\n\" # species.chemical_symbols\n \"_atom_site.label_atom_id\\n\" # species.checmical_symbols symbol + number\n # For these next keys, see the comment above.\n # \"_atom_site.label_asym_id\\n\" # Will be set to \"A\" _struct_asym.id above\n # \"_atom_site.label_comp_id\\n\" # Will be set to \"X\" _chem_comp.id above\n # \"_atom_site.label_entity_id\\n\" # Will be set to \"1\" _entity.id above\n # \"_atom_site.label_seq_id\\n\"\n \"_atom_site.occupancy\\n\" # species.concentration\n f\"_atom_site.{coord_type}_x\\n\" # cartesian_site_positions\n f\"_atom_site.{coord_type}_y\\n\" # cartesian_site_positions\n f\"_atom_site.{coord_type}_z\\n\" # cartesian_site_positions\n \"_atom_site.thermal_displace_type\\n\" # Set to 'Biso'\n \"_atom_site.B_iso_or_equiv\\n\" # Set to 1.0:f\n )\n\n if coord_type == \"fract\":\n sites = attributes.fractional_site_positions\n else:\n sites = attributes.cartesian_site_positions\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type: ignore[union-attr]\n }\n\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n label = f\"{species_name.upper()}{site_number + 1}\"\n if len(current_species.chemical_symbols) > 1:\n if (\n \"vacancy\" in current_species.chemical_symbols\n and len(current_species.chemical_symbols) == 2\n ):\n pass\n else:\n label = f\"{symbol.upper()}{index + 1}\"\n\n cif += (\n f\"ATOM {site_number + 1:5d} {symbol} {label:8} \"\n f\"{current_species.concentration[index]:6.4f} {site[0]:8.5f} \"\n f\"{site[1]:8.5f} {site[2]:8.5f} {'Biso':4} {'1.000':6}\\n\"\n )\n\n return cif\n
"},{"location":"api_reference/adapters/structures/pymatgen/","title":"pymatgen","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a pymatgen Molecule
or Structure
object.
This conversion function relies on the pymatgen package.
For more information on the pymatgen code see their documentation.
"},{"location":"api_reference/adapters/structures/pymatgen/#optimade.adapters.structures.pymatgen.from_pymatgen","title":"from_pymatgen(pmg_structure)
","text":"Convert a pymatgen Structure
(3D) into an OPTIMADE StructureResourceAttributes
model.
Parameters:
Name Type Description Defaultpmg_structure
Structure
The pymatgen Structure
to convert.
Returns:
Type DescriptionStructureResourceAttributes
An OPTIMADE StructureResourceAttributes
model, which can be converted to a raw Python dictionary with .model_dump()
or to JSON with .model_dump_json()
.
optimade/adapters/structures/pymatgen.py
def from_pymatgen(pmg_structure: Structure) -> StructureResourceAttributes:\n \"\"\"Convert a pymatgen `Structure` (3D) into an OPTIMADE `StructureResourceAttributes` model.\n\n Parameters:\n pmg_structure: The pymatgen `Structure` to convert.\n\n Returns:\n An OPTIMADE `StructureResourceAttributes` model, which can be converted to a raw Python\n dictionary with `.model_dump()` or to JSON with `.model_dump_json()`.\n\n \"\"\"\n\n if not isinstance(pmg_structure, Structure):\n raise RuntimeError(\n f\"Cannot convert type {type(pmg_structure)} into an OPTIMADE `StructureResourceAttributes` model.\"\n )\n\n attributes = {}\n attributes[\"cartesian_site_positions\"] = pmg_structure.lattice.get_cartesian_coords(\n pmg_structure.frac_coords\n ).tolist()\n attributes[\"lattice_vectors\"] = pmg_structure.lattice.matrix.tolist()\n attributes[\"species_at_sites\"] = [_.symbol for _ in pmg_structure.species]\n attributes[\"species\"] = [\n {\"name\": _.symbol, \"chemical_symbols\": [_.symbol], \"concentration\": [1]}\n for _ in set(pmg_structure.composition.elements)\n ]\n attributes[\"dimension_types\"] = [int(_) for _ in pmg_structure.lattice.pbc]\n attributes[\"nperiodic_dimensions\"] = sum(attributes[\"dimension_types\"])\n attributes[\"nelements\"] = len(pmg_structure.composition.elements)\n attributes[\"chemical_formula_anonymous\"] = anonymize_formula(\n pmg_structure.composition.formula\n )\n attributes[\"elements\"] = sorted(\n [_.symbol for _ in pmg_structure.composition.elements]\n )\n attributes[\"chemical_formula_reduced\"] = reduce_formula(\n pmg_structure.composition.formula\n )\n attributes[\"chemical_formula_descriptive\"] = pmg_structure.composition.formula\n attributes[\"elements_ratios\"] = [\n pmg_structure.composition.get_atomic_fraction(e) for e in attributes[\"elements\"]\n ]\n attributes[\"nsites\"] = len(attributes[\"species_at_sites\"])\n\n attributes[\"last_modified\"] = None\n attributes[\"immutable_id\"] = None\n attributes[\"structure_features\"] = []\n\n return StructureResourceAttributes(**attributes)\n
"},{"location":"api_reference/adapters/structures/pymatgen/#optimade.adapters.structures.pymatgen.get_pymatgen","title":"get_pymatgen(optimade_structure)
","text":"Get pymatgen Structure
or Molecule
from OPTIMADE structure.
This function will return either a pymatgen Structure
or Molecule
based on the periodicity or periodic dimensionality of OPTIMADE structure.
For structures that are periodic in one or more dimensions, a pymatgen Structure
is returned when valid lattice_vectors are given. This means, if the any of the values in the dimension_types
attribute is 1
s or if nperiodic_dimesions
> 0.
Otherwise, a pymatgen Molecule
is returned.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionUnion[Structure, Molecule]
A pymatgen Structure
or Molecule
based on the periodicity of the
Union[Structure, Molecule]
OPTIMADE structure.
Source code inoptimade/adapters/structures/pymatgen.py
def get_pymatgen(optimade_structure: OptimadeStructure) -> Union[Structure, Molecule]:\n \"\"\"Get pymatgen `Structure` or `Molecule` from OPTIMADE structure.\n\n This function will return either a pymatgen `Structure` or `Molecule` based\n on the periodicity or periodic dimensionality of OPTIMADE structure.\n\n For structures that are periodic in one or more dimensions, a pymatgen `Structure` is returned when valid lattice_vectors are given.\n This means, if the any of the values in the [`dimension_types`][optimade.models.structures.StructureResourceAttributes.dimension_types]\n attribute is `1`s or if [`nperiodic_dimesions`][optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions] > 0.\n\n Otherwise, a pymatgen `Molecule` is returned.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A pymatgen `Structure` or `Molecule` based on the periodicity of the\n OPTIMADE structure.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Structure\")):\n warn(PYMATGEN_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n if valid_lattice_vector(optimade_structure.attributes.lattice_vectors) and ( # type: ignore[arg-type]\n optimade_structure.attributes.nperiodic_dimensions > 0 # type: ignore[operator]\n or any(optimade_structure.attributes.dimension_types) # type: ignore[arg-type]\n ):\n return _get_structure(optimade_structure)\n\n return _get_molecule(optimade_structure)\n
"},{"location":"api_reference/adapters/structures/utils/","title":"utils","text":"Utility functions to help the conversion functions along.
Most of these functions rely on the NumPy library.
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.cell_to_cellpar","title":"cell_to_cellpar(cell, radians=False)
","text":"Returns the cell parameters [a, b, c, alpha, beta, gamma]
.
Angles are in degrees unless radian=True
is used.
Based on ASE code.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
radians
bool
Use radians instead of degrees (default) for angles.
False
Returns:
Type Descriptionlist[float]
The unit cell parameters as a list
of float
values.
optimade/adapters/structures/utils.py
def cell_to_cellpar(\n cell: tuple[Vector3D, Vector3D, Vector3D], radians: bool = False\n) -> list[float]:\n \"\"\"Returns the cell parameters `[a, b, c, alpha, beta, gamma]`.\n\n Angles are in degrees unless `radian=True` is used.\n\n Note:\n Based on [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/geometry/cell.html#cell_to_cellpar).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n radians: Use radians instead of degrees (default) for angles.\n\n Returns:\n The unit cell parameters as a `list` of `float` values.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell = np.asarray(cell)\n\n lengths = [np.linalg.norm(vector) for vector in cell]\n angles = []\n for i in range(3):\n j = i - 1\n k = i - 2\n outer_product = lengths[j] * lengths[k]\n if outer_product > 1e-16:\n x_vector = np.dot(cell[j], cell[k]) / outer_product\n angle = 180.0 / np.pi * np.arccos(x_vector)\n else:\n angle = 90.0\n angles.append(angle)\n if radians:\n angles = [angle * np.pi / 180 for angle in angles]\n return np.array(lengths + angles)\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.cellpar_to_cell","title":"cellpar_to_cell(cellpar, ab_normal=(0, 0, 1), a_direction=None)
","text":"Return a 3x3 cell matrix from cellpar=[a,b,c,alpha,beta,gamma]
.
Angles must be in degrees.
The returned cell is orientated such that a and b are normal to ab_normal
and a is parallel to the projection of a_direction
in the a-b plane.
Default a_direction
is (1,0,0), unless this is parallel to ab_normal
, in which case default a_direction
is (0,0,1).
The returned cell has the vectors va, vb and vc along the rows. The cell will be oriented such that va and vb are normal to ab_normal
and va will be along the projection of a_direction
onto the a-b plane.
cell = cellpar_to_cell([1, 2, 4, 10, 20, 30], (0, 1, 1), (1, 2, 3)) np.round(cell, 3) array([[ 0.816, -0.408, 0.408], [ 1.992, -0.13 , 0.13 ], [ 3.859, -0.745, 0.745]])
NoteDirect copy of ASE code.
Parameters:
Name Type Description Defaultcellpar
list[float]
The unit cell parameters as a list
of float
values.
Note: The angles must be given in degrees.
requiredab_normal
tuple[int, int, int]
Unit vector normal to the ab-plane.
(0, 0, 1)
a_direction
Optional[tuple[int, int, int]]
Unit vector defining the a-direction (default: (1, 0, 0)
).
None
Returns:
Type Descriptionlist[Vector3D]
A Cartesian 3x3 cell.
list[Vector3D]
This should equate to the
list[Vector3D]
lattice_vectors
attribute.
optimade/adapters/structures/utils.py
def cellpar_to_cell(\n cellpar: list[float],\n ab_normal: tuple[int, int, int] = (0, 0, 1),\n a_direction: Optional[tuple[int, int, int]] = None,\n) -> list[Vector3D]:\n \"\"\"Return a 3x3 cell matrix from `cellpar=[a,b,c,alpha,beta,gamma]`.\n\n Angles must be in degrees.\n\n The returned cell is orientated such that a and b\n are normal to `ab_normal` and a is parallel to the projection of\n `a_direction` in the a-b plane.\n\n Default `a_direction` is (1,0,0), unless this is parallel to\n `ab_normal`, in which case default `a_direction` is (0,0,1).\n\n The returned cell has the vectors va, vb and vc along the rows. The\n cell will be oriented such that va and vb are normal to `ab_normal`\n and va will be along the projection of `a_direction` onto the a-b\n plane.\n\n Example:\n >>> cell = cellpar_to_cell([1, 2, 4, 10, 20, 30], (0, 1, 1), (1, 2, 3))\n >>> np.round(cell, 3)\n array([[ 0.816, -0.408, 0.408],\n [ 1.992, -0.13 , 0.13 ],\n [ 3.859, -0.745, 0.745]])\n\n Note:\n Direct copy of [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/geometry/cell.html#cellpar_to_cell).\n\n Parameters:\n cellpar: The unit cell parameters as a `list` of `float` values.\n\n **Note**: The angles must be given in degrees.\n ab_normal: Unit vector normal to the ab-plane.\n a_direction: Unit vector defining the a-direction (default: `(1, 0, 0)`).\n\n Returns:\n A Cartesian 3x3 cell.\n\n This should equate to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n if a_direction is None:\n if np.linalg.norm(np.cross(ab_normal, (1, 0, 0))) < 1e-5:\n a_direction = (0, 0, 1)\n else:\n a_direction = (1, 0, 0)\n\n # Define rotated X,Y,Z-system, with Z along ab_normal and X along\n # the projection of a_direction onto the normal plane of Z.\n a_direction_array = np.array(a_direction)\n Z = unit_vector(ab_normal) # type: ignore\n X = unit_vector(a_direction_array - np.dot(a_direction_array, Z) * Z)\n Y = np.cross(Z, X)\n\n # Express va, vb and vc in the X,Y,Z-system\n alpha, beta, gamma = 90.0, 90.0, 90.0\n if isinstance(cellpar, (int, float)):\n a = b = c = cellpar\n elif len(cellpar) == 1:\n a = b = c = cellpar[0]\n elif len(cellpar) == 3:\n a, b, c = cellpar\n else:\n a, b, c, alpha, beta, gamma = cellpar\n\n # Handle orthorhombic cells separately to avoid rounding errors\n eps = 2 * np.spacing(90.0, dtype=np.float64) # around 1.4e-14\n # alpha\n if abs(abs(alpha) - 90) < eps:\n cos_alpha = 0.0\n else:\n cos_alpha = np.cos(alpha * np.pi / 180.0)\n # beta\n if abs(abs(beta) - 90) < eps:\n cos_beta = 0.0\n else:\n cos_beta = np.cos(beta * np.pi / 180.0)\n # gamma\n if abs(gamma - 90) < eps:\n cos_gamma = 0.0\n sin_gamma = 1.0\n elif abs(gamma + 90) < eps:\n cos_gamma = 0.0\n sin_gamma = -1.0\n else:\n cos_gamma = np.cos(gamma * np.pi / 180.0)\n sin_gamma = np.sin(gamma * np.pi / 180.0)\n\n # Build the cell vectors\n va = a * np.array([1, 0, 0])\n vb = b * np.array([cos_gamma, sin_gamma, 0])\n cx = cos_beta\n cy = (cos_alpha - cos_beta * cos_gamma) / sin_gamma\n cz_sqr = 1.0 - cx * cx - cy * cy\n assert cz_sqr >= 0\n cz = np.sqrt(cz_sqr)\n vc = c * np.array([cx, cy, cz])\n\n # Convert to the Cartesian x,y,z-system\n abc = np.vstack((va, vb, vc))\n T = np.vstack((X, Y, Z))\n cell = np.dot(abc, T)\n\n return cell\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.elements_ratios_from_species_at_sites","title":"elements_ratios_from_species_at_sites(species_at_sites)
","text":"Compute the OPTIMADE elements_ratios
field from species_at_sites
in the case where species_at_sites
refers to sites wholly occupied by the given elements, e.g., not arbitrary species labels or with partial/mixed occupancy.
optimade/adapters/structures/utils.py
def elements_ratios_from_species_at_sites(species_at_sites: list[str]) -> list[float]:\n \"\"\"Compute the OPTIMADE `elements_ratios` field from `species_at_sites` in the case where `species_at_sites` refers\n to sites wholly occupied by the given elements, e.g., not arbitrary species labels or with partial/mixed occupancy.\n\n \"\"\"\n elements = set(species_at_sites)\n counts = {e: species_at_sites.count(e) for e in elements}\n num_sites = len(species_at_sites)\n return [counts[e] / num_sites for e in sorted(elements)]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.fractional_coordinates","title":"fractional_coordinates(cell, cartesian_positions)
","text":"Returns fractional coordinates and wraps coordinates to [0,1[
.
Based on ASE code.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
cartesian_positions
list[Vector3D]
A list of cartesian atomic positions. This equates to the cartesian_site_positions
attribute.
Returns:
Type Descriptionlist[Vector3D]
A list of fractional coordinates for the atomic positions.
Source code inoptimade/adapters/structures/utils.py
def fractional_coordinates(\n cell: tuple[Vector3D, Vector3D, Vector3D], cartesian_positions: list[Vector3D]\n) -> list[Vector3D]:\n \"\"\"Returns fractional coordinates and wraps coordinates to `[0,1[`.\n\n Note:\n Based on [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/atoms.html#Atoms.get_scaled_positions).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n cartesian_positions: A list of cartesian atomic positions. This equates to the\n [`cartesian_site_positions`][optimade.models.structures.StructureResourceAttributes.cartesian_site_positions]\n attribute.\n\n Returns:\n A list of fractional coordinates for the atomic positions.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell_array = np.asarray(cell)\n cartesian_positions_array = np.asarray(cartesian_positions)\n\n fractional = np.linalg.solve(cell_array.T, cartesian_positions_array.T).T\n\n # Expecting a bulk 3D structure here, note, this may change in the future.\n # See `ase.atoms:Atoms.get_scaled_positions()` for ideas on how to handle lower dimensional structures.\n # Furthermore, according to ASE we need to modulo 1.0 twice.\n # This seems to be due to small floats % 1.0 becomes 1.0, hence twice makes it 0.0.\n for i in range(3):\n fractional[:, i] %= 1.0\n fractional[:, i] %= 1.0\n\n return [tuple(position) for position in fractional] # type: ignore\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.pad_cell","title":"pad_cell(lattice_vectors, padding=None)
","text":"Turn any null
/None
values into a float
in given tuple
of lattice_vectors
.
Parameters:
Name Type Description Defaultlattice_vectors
tuple[Vector3D, Vector3D, Vector3D]
A 3x3 cartesian cell. This is the lattice_vectors
attribute.
padding
Optional[float]
A value with which null
or None
values should be replaced.
None
Returns:
Type Descriptiontuple
The possibly redacted/padded lattice_vectors
and a bool
declaring whether or not
tuple
the value has been redacted/padded or not, i.e., whether it contained null
or None
tuple
values.
Source code inoptimade/adapters/structures/utils.py
def pad_cell(\n lattice_vectors: tuple[Vector3D, Vector3D, Vector3D],\n padding: Optional[float] = None,\n) -> tuple: # Setting this properly makes MkDocs fail.\n \"\"\"Turn any `null`/`None` values into a `float` in given `tuple` of\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors].\n\n Parameters:\n lattice_vectors: A 3x3 cartesian cell. This is the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors]\n attribute.\n padding: A value with which `null` or `None` values should be replaced.\n\n Returns:\n The possibly redacted/padded `lattice_vectors` and a `bool` declaring whether or not\n the value has been redacted/padded or not, i.e., whether it contained `null` or `None`\n values.\n\n \"\"\"\n return _pad_iter_of_iters(\n iterable=lattice_vectors,\n padding=padding,\n outer=tuple,\n inner=tuple,\n )\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.scaled_cell","title":"scaled_cell(cell)
","text":"Return a scaled 3x3 cell from cartesian 3x3 cell (lattice_vectors
). This 3x3 matrix can be used to calculate the fractional coordinates from the cartesian_site_positions.
This is based on PDB's method of calculating SCALE from CRYST data. For more info, see this site.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
Returns:
Type Descriptiontuple[Vector3D, Vector3D, Vector3D]
A scaled 3x3 cell.
Source code inoptimade/adapters/structures/utils.py
def scaled_cell(\n cell: tuple[Vector3D, Vector3D, Vector3D],\n) -> tuple[Vector3D, Vector3D, Vector3D]:\n \"\"\"Return a scaled 3x3 cell from cartesian 3x3 cell (`lattice_vectors`).\n This 3x3 matrix can be used to calculate the fractional coordinates from the cartesian_site_positions.\n\n This is based on PDB's method of calculating SCALE from CRYST data.\n For more info, see [this site](https://www.wwpdb.org/documentation/file-format-content/format33/sect8.html#SCALEn).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n\n Returns:\n A scaled 3x3 cell.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell = np.asarray(cell)\n\n volume = np.dot(cell[0], np.cross(cell[1], cell[2]))\n scale = []\n for i in range(3):\n vector = np.cross(cell[(i + 1) % 3], cell[(i + 2) % 3]) / volume\n scale.append(tuple(vector))\n return tuple(scale) # type: ignore[return-value]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.species_from_species_at_sites","title":"species_from_species_at_sites(species_at_sites)
","text":"When a list of species dictionaries is not provided, this function can be used to infer the species from the provided species_at_sites.
In this use case, species_at_sites is assumed to provide a list of element symbols, and refers to situations with no mixed occupancy, i.e., the constructed species list will contain all unique species with concentration equal to 1 and the species_at_site tag will be used as the chemical symbol.
Parameters:
Name Type Description Defaultspecies_at_sites
list[str]
The list found under the species_at_sites field.
requiredReturns:
Type Descriptionlist[Species]
An OPTIMADE species list.
Source code inoptimade/adapters/structures/utils.py
def species_from_species_at_sites(\n species_at_sites: list[str],\n) -> list[OptimadeStructureSpecies]:\n \"\"\"When a list of species dictionaries is not provided, this function\n can be used to infer the species from the provided species_at_sites.\n\n In this use case, species_at_sites is assumed to provide a list of\n element symbols, and refers to situations with no mixed occupancy, i.e.,\n the constructed species list will contain all unique species with\n concentration equal to 1 and the species_at_site tag will be used as\n the chemical symbol.\n\n Parameters:\n species_at_sites: The list found under the species_at_sites field.\n\n Returns:\n An OPTIMADE species list.\n\n \"\"\"\n return [\n OptimadeStructureSpecies(name=_, concentration=[1.0], chemical_symbols=[_])\n for _ in set(species_at_sites)\n ]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.unit_vector","title":"unit_vector(x)
","text":"Return a unit vector in the same direction as x
.
Parameters:
Name Type Description Defaultx
Vector3D
A three-dimensional vector.
requiredReturns:
Type DescriptionVector3D
A unit vector in the same direction as x
.
optimade/adapters/structures/utils.py
def unit_vector(x: Vector3D) -> Vector3D:\n \"\"\"Return a unit vector in the same direction as `x`.\n\n Parameters:\n x: A three-dimensional vector.\n\n Returns:\n A unit vector in the same direction as `x`.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n y = np.array(x, dtype=\"float\")\n return y / np.linalg.norm(y) # type: ignore\n
"},{"location":"api_reference/client/cli/","title":"cli","text":""},{"location":"api_reference/client/client/","title":"client","text":"This module implements OPTIMADE client functionality for:
OptimadeClient
","text":"This class implemements a client for executing the same queries across multiple OPTIMADE APIs simultaneously, paging and caching the results.
By default, all registered OPTIMADE providers will be queried simulateneously and asynchronously, with the results collected into the all_results
attribute, keyed by endpoint, filter and provider.
optimade/client/client.py
class OptimadeClient:\n \"\"\"This class implemements a client for executing the same queries\n across multiple OPTIMADE APIs simultaneously, paging and caching the\n results.\n\n By default, all registered OPTIMADE providers will be queried\n simulateneously and asynchronously, with the results collected\n into the `all_results` attribute, keyed by endpoint, filter\n and provider.\n\n \"\"\"\n\n base_urls: Union[str, Iterable[str]]\n \"\"\"A list (or any iterable) of OPTIMADE base URLs to query.\"\"\"\n\n all_results: dict[str, dict[str, dict[str, QueryResults]]] = defaultdict(dict)\n \"\"\"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains\n the results from each base URL for that particular filter.\n \"\"\"\n\n count_results: dict[str, dict[str, dict[str, int]]] = defaultdict(dict)\n \"\"\"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains\n the number of results from each base URL for that particular filter.\n \"\"\"\n\n max_results_per_provider: Optional[int] = None\n \"\"\"Maximum number of results to downlod per provider. If None, will\n download all.\n \"\"\"\n\n property_lists: dict[str, dict[str, list[str]]] = defaultdict(dict)\n \"\"\"A dictionary containing list of properties served by each database,\n broken down by entry type, then database.\n \"\"\"\n\n headers: dict = {\"User-Agent\": f\"optimade-python-tools/{__version__}\"}\n \"\"\"Additional HTTP headers.\"\"\"\n\n http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0)\n \"\"\"The timeout to use for each HTTP request.\"\"\"\n\n max_attempts: int\n \"\"\"The maximum number of times to repeat a failed query before giving up.\"\"\"\n\n use_async: bool\n \"\"\"Whether or not to make all requests asynchronously using asyncio.\"\"\"\n\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None\n \"\"\"A list of callbacks to execute after each successful request, used\n to e.g., write to a file, add results to a database or perform additional\n filtering.\n\n The callbacks will receive the request URL and the results extracted\n from the JSON response, with keys 'data', 'meta', 'links', 'errors'\n and 'included'.\n\n Each callback can return a dictionary that can modify the `next_url` with the\n key `next` and the progress bar with the key `advance_results`.\n In the case of multiple provided callbacks, only the value returned by the final\n callback in the stack will be used.\n\n \"\"\"\n\n count_binary_search: bool = True\n \"\"\"Enable binary search count for databases that do not support `meta->data_returned`.\"\"\"\n\n silent: bool\n \"\"\"Whether to disable progress bar printing.\"\"\"\n\n skip_ssl: bool = False\n \"\"\"Whether to skip SSL verification.\"\"\"\n\n _excluded_providers: Optional[set[str]] = None\n \"\"\"A set of providers IDs excluded from future queries.\"\"\"\n\n _included_providers: Optional[set[str]] = None\n \"\"\"A set of providers IDs included from future queries.\"\"\"\n\n _excluded_databases: Optional[set[str]] = None\n \"\"\"A set of child database URLs excluded from future queries.\"\"\"\n\n __current_endpoint: Optional[str] = None\n \"\"\"Used internally when querying via `client.structures.get()` to set the\n chosen endpoint. Should be reset to `None` outside of all `get()` calls.\"\"\"\n\n _http_client: Optional[Union[type[httpx.AsyncClient], type[requests.Session]]] = (\n None\n )\n \"\"\"Override the HTTP client class, primarily used for testing.\"\"\"\n\n __strict_async: bool = False\n \"\"\"Whether or not to fallover if `use_async` is true yet asynchronous mode\n is impossible due to, e.g., a running event loop.\n \"\"\"\n\n _force_binary_search: bool = False\n \"\"\"Setting to test binary searches in cases where servers do return\n the count.\n \"\"\"\n\n def __init__(\n self,\n base_urls: Optional[Union[str, Iterable[str]]] = None,\n max_results_per_provider: int = 1000,\n headers: Optional[dict] = None,\n http_timeout: Optional[Union[httpx.Timeout, float]] = None,\n max_attempts: int = 5,\n use_async: bool = True,\n silent: bool = False,\n exclude_providers: Optional[list[str]] = None,\n include_providers: Optional[list[str]] = None,\n exclude_databases: Optional[list[str]] = None,\n http_client: Optional[\n Union[type[httpx.AsyncClient], type[requests.Session]]\n ] = None,\n verbosity: int = 0,\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None,\n skip_ssl: bool = False,\n ):\n \"\"\"Create the OPTIMADE client object.\n\n Parameters:\n base_urls: A list of OPTIMADE base URLs to query.\n max_results_per_provider: The maximum number of results to download\n from each provider (-1 or 0 indicate unlimited).\n headers: Any additional HTTP headers to use for the queries.\n http_timeout: The timeout to use per request. Defaults to 10\n seconds with 1000 seconds for reads specifically. Overriding this value\n will replace all timeouts (connect, read, write and pool) with this value.\n max_attempts: The maximum number of times to repeat a failing query.\n use_async: Whether or not to make all requests asynchronously.\n exclude_providers: A set or collection of provider IDs to exclude from queries.\n include_providers: A set or collection of provider IDs to include in queries.\n exclude_databases: A set or collection of child database URLs to exclude from queries.\n http_client: An override for the underlying HTTP client, primarily used for testing.\n callbacks: A list of functions to call after each successful response, see the\n attribute [`OptimadeClient.callbacks`][optimade.client.OptimadeClient.callbacks]\n docstring for more details.\n verbosity: The verbosity level of the client.\n\n \"\"\"\n\n self.max_results_per_provider = max_results_per_provider\n if self.max_results_per_provider in (-1, 0):\n self.max_results_per_provider = None\n\n self._excluded_providers = set(exclude_providers) if exclude_providers else None\n self._included_providers = set(include_providers) if include_providers else None\n self._excluded_databases = set(exclude_databases) if exclude_databases else None\n\n self.max_attempts = max_attempts\n self.silent = silent\n self.verbosity = verbosity\n self.skip_ssl = skip_ssl\n\n if headers:\n self.headers.update(headers)\n\n if not base_urls:\n progress = None\n if not self.silent:\n progress = OptimadeClientProgress()\n self.base_urls = list(\n get_all_databases(\n exclude_providers=self._excluded_providers,\n include_providers=self._included_providers,\n exclude_databases=self._excluded_databases,\n progress=progress,\n skip_ssl=self.skip_ssl,\n )\n )\n else:\n if exclude_providers or include_providers or exclude_databases:\n raise RuntimeError(\n \"Cannot provide both a list of base URLs and included/excluded databases.\"\n )\n\n self.base_urls = base_urls\n\n if isinstance(self.base_urls, str):\n self.base_urls = [self.base_urls]\n self.base_urls = list(self.base_urls)\n\n if not self.base_urls:\n raise SystemExit(\n \"Unable to access any OPTIMADE base URLs. If you believe this is an error, try manually specifying some base URLs.\"\n )\n\n if http_timeout:\n if isinstance(http_timeout, httpx.Timeout):\n self.http_timeout = http_timeout\n else:\n self.http_timeout = httpx.Timeout(http_timeout)\n\n self.use_async = use_async\n\n if http_client:\n self._http_client = http_client\n if issubclass(self._http_client, httpx.AsyncClient):\n if not self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use synchronous mode with an asynchronous HTTP client, please set `use_async=True` or pass an asynchronous HTTP client.\"\n )\n self.use_async = True\n elif issubclass(self._http_client, requests.Session):\n if self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use async mode with a synchronous HTTP client, please set `use_async=False` or pass an synchronous HTTP client.\"\n )\n self.use_async = False\n else:\n if use_async:\n self._http_client = httpx.AsyncClient\n else:\n self._http_client = requests.Session\n\n self.callbacks = callbacks\n\n def __getattribute__(self, name):\n \"\"\"Allows entry endpoints to be queried via attribute access, using the\n allowed list for this module.\n\n Should also pass through any `extensions/<example>` endpoints.\n\n Any non-entry-endpoint name requested will be passed to the\n original `__getattribute__`.\n\n !!! example\n ```python\n from optimade.client import OptimadeClient\n cli = OptimadeClient()\n structures = cli.structures.get()\n references = cli.references.get()\n info_structures = cli.info.structures.get()\n ```\n\n \"\"\"\n if name in ENDPOINTS:\n if self.__current_endpoint == \"info\":\n self.__current_endpoint = f\"info/{name}\"\n elif self.__current_endpoint == \"extensions\":\n self.__current_endpoint = f\"extensions/{name}\"\n else:\n self.__current_endpoint = name\n return self\n\n return super().__getattribute__(name)\n\n def get(\n self,\n filter: Optional[str] = None,\n endpoint: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n ) -> dict[str, dict[str, dict[str, dict]]]:\n \"\"\"Gets the results from the endpoint and filter across the\n defined OPTIMADE APIs.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Performing query [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n response_fields=response_fields,\n page_limit=None,\n paginate=True,\n sort=sort,\n )\n self.all_results[endpoint][filter] = results\n return {endpoint: {filter: {k: results[k].asdict() for k in results}}}\n\n def count(\n self, filter: Optional[str] = None, endpoint: Optional[str] = None\n ) -> dict[str, dict[str, dict[str, Optional[int]]]]:\n \"\"\"Counts the number of results for the filter, requiring\n only 1 request per provider by making use of the `meta->data_returned`\n key. If missing, attempts will be made to perform an exponential/binary\n search over pagination to count the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the number of query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Counting results for [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n page_limit=1,\n paginate=False,\n response_fields=[],\n sort=None,\n )\n count_results = {}\n\n for base_url in results:\n count_results[base_url] = results[base_url].meta.get(\n \"data_returned\", None\n )\n\n if count_results[base_url] is None or self._force_binary_search:\n if self.count_binary_search:\n count_results[base_url] = self.binary_search_count(\n filter, endpoint, base_url, results\n )\n else:\n self._progress.print(\n f\"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results. Full response: {results[base_url]}\"\n )\n\n self.count_results[endpoint][filter] = count_results\n return {endpoint: {filter: count_results}}\n\n def binary_search_count(\n self, filter: str, endpoint: str, base_url: str, results: Optional[dict] = None\n ) -> int:\n \"\"\"In cases where `data_returned` is not available (due to database limitations or\n otherwise), iteratively probe the final page of results available for a filter using\n binary search.\n\n Note: These queries always happen synchronously across APIs, but can be executed\n asynchronously within a single API.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n results: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if self.verbosity:\n self._progress.print(f\"Performing binary search count for {base_url}\")\n if self.use_async:\n return self._binary_search_count_async(filter, endpoint, base_url, results)\n\n else:\n raise NotImplementedError(\n \"Binary search count is not yet implemented for synchronous queries.\"\n )\n\n def _binary_search_count_async(\n self, filter: str, endpoint: str, base_url: str, result: Optional[dict] = None\n ) -> int:\n \"\"\"Run a series of asynchronously queries on a given API to\n find the number of results for a filter.\n\n Starting with logarithmically spaced page offsets, iteratively probe\n the final page of results available for a filter.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n result: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if result is None:\n # first a check that there are any results at all\n result = asyncio.run(\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=1,\n response_fields=[],\n paginate=False,\n )\n )\n if self.verbosity:\n self._progress.print(\"Definitely found results\")\n\n if not result[base_url].data:\n return 0\n\n attempts = 0\n max_attempts = 100\n\n window, probe = self._update_probe_and_window()\n\n while attempts < max_attempts:\n self._progress.disable = True\n\n result = asyncio.run(\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=1,\n response_fields=[],\n paginate=False,\n other_params={\"page_offset\": probe},\n )\n )\n\n self._progress.disable = self.silent\n\n window, probe = self._update_probe_and_window(\n window, probe, bool(result[base_url].data)\n )\n\n if window[0] == window[1] and window[0] == probe:\n return probe\n\n attempts += 1\n\n if self.verbosity > 2:\n self._progress.print(f\"Binary search debug info: {window=}, {probe=}\")\n\n else:\n message = f\"Exceeded maximum number of attempts for binary search on {base_url}, {filter=}\"\n self._progress.print(message)\n raise RuntimeError(message)\n\n @staticmethod\n def _update_probe_and_window(\n window: Optional[tuple[int, Optional[int]]] = None,\n last_probe: Optional[int] = None,\n below: Optional[bool] = None,\n ) -> tuple[tuple[int, Optional[int]], int]:\n \"\"\"Sets the new range, trial value and exit condition for exponential/binary search.\n When converged, returns the same value three times.\n\n Parameters:\n window: The current window of results.\n last_probe: The last probe value.\n below: Whether the last probe was below the target value.\n\n Returns:\n A tuple of the new window and probe value,\n or the count three times if converged.\n\n \"\"\"\n\n if window is None and last_probe is None:\n return (1, None), 1_000_000\n\n if window is None or last_probe is None:\n raise RuntimeError(\n \"Invalid arguments: must provide all or none of window, last_probe and below parameters\"\n )\n\n probe: int = last_probe\n\n # Exit condition: find a range of (count, count+1) values\n # and determine whether the probe was above or below in the last guess\n if window[1] is not None and window[1] - window[0] == 1:\n if below:\n return (window[0], window[0]), window[0]\n else:\n return (window[1], window[1]), window[1]\n\n # Enclose the real value in the window, with `None` indicating an open boundary\n if below:\n window = (last_probe, window[1])\n else:\n window = (window[0], last_probe)\n\n # If we've not reached the upper bound yet, try 10x\n if window[1] is None:\n probe *= 10\n\n # Otherwise, if we're in the window and the ends of the window now have the same power of 10, take the average (102 => 108) => 105\n elif round(math.log10(window[0])) == round(math.log10(window[0])):\n probe = (window[1] + window[0]) // 2\n # otherwise use logarithmic average (10, 1000) => 100\n else:\n probe = int(10 ** (math.log10(window[1]) + math.log10(window[0]) / 2))\n\n return window, probe\n\n def list_properties(\n self,\n entry_type: str,\n ) -> dict[str, list[str]]:\n \"\"\"Returns the list of properties reported at `/info/<entry_type>`\n for the given entry type, for each database.\n\n \"\"\"\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Listing properties for [bold yellow]{entry_type}[/bold yellow]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n \"\",\n f\"info/{entry_type}\",\n paginate=False,\n page_limit=1,\n response_fields=[],\n sort=None,\n )\n self.property_lists = {entry_type: {}}\n for database in results:\n self.property_lists[entry_type][database] = list(\n results[database].data.get(\"properties\", {}).keys() # type: ignore\n )\n return self.property_lists[entry_type]\n\n def search_property(self, query: str, entry_type: str) -> dict[str, list[str]]:\n \"\"\"Searches for the query substring within the listed properties\n served by each database.\n\n Parameters:\n query: The substring to search for.\n entry_type: The entry type to query.\n\n Returns:\n A nested dictionary of matching property lists, arranged by\n entry type and database.\n\n \"\"\"\n if not self.property_lists:\n self.list_properties(entry_type=entry_type)\n\n matching_properties: dict[str, dict[str, list[str]]] = {\n entry_type: defaultdict(list)\n }\n if entry_type in self.property_lists:\n for database in self.property_lists[entry_type]:\n for property in self.property_lists[entry_type][database]:\n if query in property:\n matching_properties[entry_type][database].append(property)\n return matching_properties[entry_type]\n\n def _execute_queries(\n self,\n filter: str,\n endpoint: str,\n page_limit: Optional[int],\n paginate: bool,\n response_fields: Optional[list[str]],\n sort: Optional[str],\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the queries over the base URLs either asynchronously or\n serially, depending on the `self.use_async` setting.\n\n Parameters:\n filter: The OPTIMADE filter string.\n endpoint: The OPTIMADE endpoint to query.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Returns:\n A mapping from base URL to `QueryResults` for each queried API.\n\n \"\"\"\n if self.use_async:\n # Check for a pre-existing event loop (e.g. within a Jupyter notebook)\n # and use it if present\n try:\n event_loop = asyncio.get_running_loop()\n if event_loop:\n if self.__strict_async:\n raise RuntimeError(\n \"Detected a running event loop, cannot run in async mode.\"\n )\n self._progress.print(\n \"Detected a running event loop (e.g., Jupyter). Attempting to switch to synchronous mode.\"\n )\n self.use_async = False\n self._http_client = requests.Session\n except RuntimeError:\n event_loop = None\n\n if self.use_async and not event_loop:\n return asyncio.run(\n self._get_all_async(\n endpoint,\n filter,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n )\n )\n\n return self._get_all(\n endpoint,\n filter,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n )\n\n def get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously on one API.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return self._get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]\"\n )\n return {base_url: error_query_results}\n\n async def _get_all_async(\n self,\n endpoint: str,\n filter: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n base_urls: Optional[Iterable[str]] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously across all defined APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n base_urls: A list of base URLs to query (defaults to `self.base_urls`).\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n if not base_urls:\n base_urls = self.base_urls\n\n results = await asyncio.gather(\n *[\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n for base_url in base_urls\n ]\n )\n return functools.reduce(lambda r1, r2: {**r1, **r2}, results)\n\n def _get_all(\n self,\n endpoint: str,\n filter: str,\n page_limit: Optional[int] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n paginate: bool = True,\n base_urls: Optional[Iterable[str]] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously across all defined APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n base_urls: A list of base URLs to query (defaults to `self.base_urls`).\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n if not base_urls:\n base_urls = self.base_urls\n results = [\n self.get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n for base_url in base_urls\n ]\n if results:\n return functools.reduce(lambda r1, r2: {**r1, **r2}, results)\n\n return {}\n\n async def get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously on one API.\n\n !!! note\n This method currently makes non-blocking requests\n to a single API, but these requests are executed\n serially on that API, i.e., results are pulled one\n page at a time, but requests will not block other\n async requests to other APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return await self._get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{error_query_results.errors}[/red i]\"\n )\n return {base_url: error_query_results}\n\n async def _get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"See [`OptimadeClient.get_one_async`][optimade.client.OptimadeClient.get_one_async].\"\"\"\n next_url, _task = self._setup(\n endpoint=endpoint,\n base_url=base_url,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n results = QueryResults()\n try:\n async with self._http_client(headers=self.headers) as client: # type: ignore[union-attr,call-arg,misc]\n while next_url:\n attempts = 0\n try:\n if self.verbosity:\n self._progress.print(\n f\"Making request to {next_url!r} {attempts=}\"\n )\n r = await client.get(\n next_url, follow_redirects=True, timeout=self.http_timeout\n )\n page_results, next_url = self._handle_response(r, _task)\n except RecoverableHTTPError:\n attempts += 1\n if attempts > self.max_attempts:\n raise RuntimeError(\n f\"Exceeded maximum number of retries for {next_url}\"\n )\n await asyncio.sleep(1)\n continue\n\n results.update(page_results)\n\n if not paginate:\n break\n\n if (\n self.max_results_per_provider\n and len(results.data) >= self.max_results_per_provider\n ):\n if not self.silent:\n self._progress.print(\n f\"Reached {len(results.data)} results for {base_url}, exceeding `max_results_per_provider` parameter ({self.max_results_per_provider}). Stopping download.\"\n )\n break\n\n return {str(base_url): results}\n\n finally:\n self._teardown(_task, len(results.data))\n\n def _get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n response_fields: Optional[list[str]] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"See [`OptimadeClient.get_one`][optimade.client.OptimadeClient.get_one].\"\"\"\n next_url, _task = self._setup(\n endpoint=endpoint,\n base_url=base_url,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n results = QueryResults()\n try:\n with self._http_client() as client: # type: ignore[misc]\n client.headers.update(self.headers)\n\n if isinstance(client, requests.Session):\n # Convert configured httpx timeout to requests-style tuple\n timeout = (self.http_timeout.connect, self.http_timeout.read)\n\n while next_url:\n attempts = 0\n try:\n if self.verbosity:\n self._progress.print(\n f\"Making request to {next_url!r} {attempts=}\"\n )\n r = client.get(next_url, timeout=timeout)\n page_results, next_url = self._handle_response(r, _task)\n except RecoverableHTTPError:\n attempts += 1\n if attempts > self.max_attempts:\n raise RuntimeError(\n f\"Exceeded maximum number of retries for {next_url}\"\n )\n time.sleep(1)\n continue\n\n results.update(page_results)\n\n if (\n self.max_results_per_provider\n and len(results.data) >= self.max_results_per_provider\n ):\n if not self.silent:\n self._progress.print(\n f\"Reached {len(results.data)} results for {base_url}, exceeding `max_results_per_provider` parameter ({self.max_results_per_provider}). Stopping download.\"\n )\n break\n\n if not paginate:\n break\n\n return {str(base_url): results}\n\n finally:\n self._teardown(_task, len(results.data))\n\n def _setup(\n self,\n endpoint: str,\n base_url: str,\n filter: str,\n page_limit: Optional[int],\n response_fields: Optional[list[str]],\n sort: Optional[str],\n other_params: Optional[dict[str, Any]] = None,\n ) -> tuple[str, TaskID]:\n \"\"\"Constructs the first query URL and creates the progress bar task.\n\n Returns:\n The URL for the first query and the Rich TaskID for progress logging.\n\n \"\"\"\n url = self._build_url(\n base_url=base_url,\n endpoint=endpoint,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n parsed_url = urlparse(url)\n _task = self._progress.add_task(\n description=parsed_url.netloc + parsed_url.path,\n total=None,\n )\n return url, _task\n\n def _build_url(\n self,\n base_url: str,\n endpoint: Optional[str] = \"structures\",\n version: Optional[str] = None,\n filter: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> str:\n \"\"\"Builds the URL to query based on the passed parameters.\n\n Parameters:\n base_url: The server's base URL.\n endpoint: The endpoint to query.\n version: The OPTIMADE version string.\n filter: The filter to apply to the endpoint.\n response_fields: A list of response fields to request from the server.\n sort: The field by which to sort the results.\n page_limit: The page limit for an individual request.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n The overall query URL, including parameters.\n\n \"\"\"\n\n if not version:\n version = f'v{__api_version__.split(\".\")[0]}'\n while base_url.endswith(\"/\"):\n base_url = base_url[:-1]\n\n url = f\"{base_url}/{version}/{endpoint}\"\n\n params_dict: dict[str, str] = {}\n\n if filter:\n params_dict[\"filter\"] = f\"filter={filter}\"\n if response_fields is not None:\n # If we have requested no response fields (e.g., in the case of --count) then just ask for IDs\n if len(response_fields) == 0:\n params_dict[\"response_fields\"] = \"response_fields=id\"\n else:\n params_dict[\"response_fields\"] = (\n f'response_fields={\",\".join(response_fields)}'\n )\n\n if page_limit:\n params_dict[\"page_limit\"] = f\"page_limit={page_limit}\"\n if sort:\n params_dict[\"sort\"] = f\"sort={sort}\"\n\n if other_params:\n for p in other_params:\n params_dict[p] = f\"{p}={other_params[p]}\"\n\n params = \"&\".join(p for p in params_dict.values() if p)\n if params:\n url += f\"?{params}\"\n\n return url\n\n def _check_filter(self, filter: str, endpoint: str) -> None:\n \"\"\"Passes the filter through [`LarkParser`][optimade.filterparser.LarkParser]\n from the optimade-python-tools reference server implementation.\n\n Parameters:\n filter: The filter string.\n endpoint: The endpoint being queried. If this endpoint is not \"known\" to\n OPTIMADE, the filter will automatically pass.\n\n Raises:\n RuntimeError: If the filter cannot be parsed.\n\n \"\"\"\n try:\n if endpoint in ENDPOINTS:\n parser = LarkParser()\n parser.parse(filter)\n except BadRequest as exc:\n self._progress.print(\n f\"[bold red]Filter [blue i]{filter!r}[/blue i] could not be parsed as an OPTIMADE filter.[/bold red]\",\n Panel(f\"[magenta]{exc}[/magenta]\"),\n )\n with silent_raise():\n raise RuntimeError(exc) from None\n\n def _handle_response(\n self, response: Union[httpx.Response, requests.Response], _task: TaskID\n ) -> tuple[dict[str, Any], str]:\n \"\"\"Handle the response from the server.\n\n Parameters:\n response: The response from the server.\n _task: The Rich TaskID for this task's progressbar.\n\n Returns:\n A dictionary containing the results, and a link to the next page,\n if it exists.\n\n \"\"\"\n\n # Handle error statuses\n if response.status_code == 429:\n raise TooManyRequestsException(response.content)\n if response.status_code != 200:\n try:\n errors = response.json().get(\"errors\")\n error_message = \"\\n\".join(\n [f\"{error['title']}: {error['detail']}\" for error in errors]\n )\n except Exception:\n error_message = str(response.content)\n\n raise RuntimeError(\n f\"{response.status_code} - {response.url}: {error_message}\"\n )\n\n try:\n r = response.json()\n except json.JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not decode response as JSON: {response.content!r}\"\n ) from exc\n\n # Accumulate results with correct empty containers if missing\n results = {\n \"data\": r.get(\"data\", []),\n \"meta\": r.get(\"meta\", {}),\n \"links\": r.get(\"links\", {}),\n \"included\": r.get(\"included\", []),\n \"errors\": r.get(\"errors\", []),\n }\n\n callback_response = None\n if self.callbacks:\n callback_response = self._execute_callbacks(results, response)\n callback_response = callback_response or {}\n\n # Advance the progress bar for this provider\n self._progress.update(\n _task,\n advance=callback_response.get(\"advance_results\", len(results[\"data\"])),\n total=results[\"meta\"].get(\"data_returned\", None),\n )\n\n next_url = callback_response.get(\"next\") or results[\"links\"].get(\"next\", None)\n if isinstance(next_url, dict):\n next_url = next_url.pop(\"href\")\n\n return results, next_url\n\n def _teardown(self, _task: TaskID, num_results: int) -> None:\n \"\"\"Update the finished status of the progress bar depending on the number of results.\n\n Parameters:\n _task: The Rich TaskID for this task's progressbar.\n num_results: The number of data entries returned.\n\n \"\"\"\n if num_results == 0:\n self._progress.update(_task, total=None, finished=False, complete=True)\n else:\n self._progress.update(\n _task, total=num_results, finished=True, complete=True\n )\n\n def _execute_callbacks(\n self, results: dict, response: Union[httpx.Response, requests.Response]\n ) -> Union[None, dict]:\n \"\"\"Execute any callbacks registered with the client.\n\n Parameters:\n results: The results from the query.\n response: The full response from the server.\n\n Returns:\n Either `None` or the string value returned from the *final* callback.\n\n \"\"\"\n request_url = str(response.request.url)\n if not self.callbacks:\n return None\n for callback in self.callbacks:\n cb_response = callback(request_url, results)\n return cb_response\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__current_endpoint","title":"__current_endpoint: Optional[str] = None
class-attribute
instance-attribute
","text":"Used internally when querying via client.structures.get()
to set the chosen endpoint. Should be reset to None
outside of all get()
calls.
__strict_async: bool = False
class-attribute
instance-attribute
","text":"Whether or not to fallover if use_async
is true yet asynchronous mode is impossible due to, e.g., a running event loop.
all_results: dict[str, dict[str, dict[str, QueryResults]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the results from each base URL for that particular filter.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.base_urls","title":"base_urls: Union[str, Iterable[str]]
instance-attribute
","text":"A list (or any iterable) of OPTIMADE base URLs to query.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.callbacks","title":"callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = callbacks
class-attribute
instance-attribute
","text":"A list of callbacks to execute after each successful request, used to e.g., write to a file, add results to a database or perform additional filtering.
The callbacks will receive the request URL and the results extracted from the JSON response, with keys 'data', 'meta', 'links', 'errors' and 'included'.
Each callback can return a dictionary that can modify the next_url
with the key next
and the progress bar with the key advance_results
. In the case of multiple provided callbacks, only the value returned by the final callback in the stack will be used.
count_binary_search: bool = True
class-attribute
instance-attribute
","text":"Enable binary search count for databases that do not support meta->data_returned
.
count_results: dict[str, dict[str, dict[str, int]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the number of results from each base URL for that particular filter.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.headers","title":"headers: dict = {'User-Agent': f'optimade-python-tools/{__version__}'}
class-attribute
instance-attribute
","text":"Additional HTTP headers.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.http_timeout","title":"http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0)
class-attribute
instance-attribute
","text":"The timeout to use for each HTTP request.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.max_attempts","title":"max_attempts: int = max_attempts
instance-attribute
","text":"The maximum number of times to repeat a failed query before giving up.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.max_results_per_provider","title":"max_results_per_provider: Optional[int] = max_results_per_provider
class-attribute
instance-attribute
","text":"Maximum number of results to downlod per provider. If None, will download all.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.property_lists","title":"property_lists: dict[str, dict[str, list[str]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A dictionary containing list of properties served by each database, broken down by entry type, then database.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.silent","title":"silent: bool = silent
instance-attribute
","text":"Whether to disable progress bar printing.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.skip_ssl","title":"skip_ssl: bool = skip_ssl
class-attribute
instance-attribute
","text":"Whether to skip SSL verification.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.use_async","title":"use_async: bool = use_async
instance-attribute
","text":"Whether or not to make all requests asynchronously using asyncio.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__getattribute__","title":"__getattribute__(name)
","text":"Allows entry endpoints to be queried via attribute access, using the allowed list for this module.
Should also pass through any extensions/<example>
endpoints.
Any non-entry-endpoint name requested will be passed to the original __getattribute__
.
Example
from optimade.client import OptimadeClient\ncli = OptimadeClient()\nstructures = cli.structures.get()\nreferences = cli.references.get()\ninfo_structures = cli.info.structures.get()\n
Source code in optimade/client/client.py
def __getattribute__(self, name):\n \"\"\"Allows entry endpoints to be queried via attribute access, using the\n allowed list for this module.\n\n Should also pass through any `extensions/<example>` endpoints.\n\n Any non-entry-endpoint name requested will be passed to the\n original `__getattribute__`.\n\n !!! example\n ```python\n from optimade.client import OptimadeClient\n cli = OptimadeClient()\n structures = cli.structures.get()\n references = cli.references.get()\n info_structures = cli.info.structures.get()\n ```\n\n \"\"\"\n if name in ENDPOINTS:\n if self.__current_endpoint == \"info\":\n self.__current_endpoint = f\"info/{name}\"\n elif self.__current_endpoint == \"extensions\":\n self.__current_endpoint = f\"extensions/{name}\"\n else:\n self.__current_endpoint = name\n return self\n\n return super().__getattribute__(name)\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__init__","title":"__init__(base_urls=None, max_results_per_provider=1000, headers=None, http_timeout=None, max_attempts=5, use_async=True, silent=False, exclude_providers=None, include_providers=None, exclude_databases=None, http_client=None, verbosity=0, callbacks=None, skip_ssl=False)
","text":"Create the OPTIMADE client object.
Parameters:
Name Type Description Defaultbase_urls
Optional[Union[str, Iterable[str]]]
A list of OPTIMADE base URLs to query.
None
max_results_per_provider
int
The maximum number of results to download from each provider (-1 or 0 indicate unlimited).
1000
headers
Optional[dict]
Any additional HTTP headers to use for the queries.
None
http_timeout
Optional[Union[Timeout, float]]
The timeout to use per request. Defaults to 10 seconds with 1000 seconds for reads specifically. Overriding this value will replace all timeouts (connect, read, write and pool) with this value.
None
max_attempts
int
The maximum number of times to repeat a failing query.
5
use_async
bool
Whether or not to make all requests asynchronously.
True
exclude_providers
Optional[list[str]]
A set or collection of provider IDs to exclude from queries.
None
include_providers
Optional[list[str]]
A set or collection of provider IDs to include in queries.
None
exclude_databases
Optional[list[str]]
A set or collection of child database URLs to exclude from queries.
None
http_client
Optional[Union[type[AsyncClient], type[Session]]]
An override for the underlying HTTP client, primarily used for testing.
None
callbacks
Optional[list[Callable[[str, dict], Union[None, dict]]]]
A list of functions to call after each successful response, see the attribute OptimadeClient.callbacks
docstring for more details.
None
verbosity
int
The verbosity level of the client.
0
Source code in optimade/client/client.py
def __init__(\n self,\n base_urls: Optional[Union[str, Iterable[str]]] = None,\n max_results_per_provider: int = 1000,\n headers: Optional[dict] = None,\n http_timeout: Optional[Union[httpx.Timeout, float]] = None,\n max_attempts: int = 5,\n use_async: bool = True,\n silent: bool = False,\n exclude_providers: Optional[list[str]] = None,\n include_providers: Optional[list[str]] = None,\n exclude_databases: Optional[list[str]] = None,\n http_client: Optional[\n Union[type[httpx.AsyncClient], type[requests.Session]]\n ] = None,\n verbosity: int = 0,\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None,\n skip_ssl: bool = False,\n):\n \"\"\"Create the OPTIMADE client object.\n\n Parameters:\n base_urls: A list of OPTIMADE base URLs to query.\n max_results_per_provider: The maximum number of results to download\n from each provider (-1 or 0 indicate unlimited).\n headers: Any additional HTTP headers to use for the queries.\n http_timeout: The timeout to use per request. Defaults to 10\n seconds with 1000 seconds for reads specifically. Overriding this value\n will replace all timeouts (connect, read, write and pool) with this value.\n max_attempts: The maximum number of times to repeat a failing query.\n use_async: Whether or not to make all requests asynchronously.\n exclude_providers: A set or collection of provider IDs to exclude from queries.\n include_providers: A set or collection of provider IDs to include in queries.\n exclude_databases: A set or collection of child database URLs to exclude from queries.\n http_client: An override for the underlying HTTP client, primarily used for testing.\n callbacks: A list of functions to call after each successful response, see the\n attribute [`OptimadeClient.callbacks`][optimade.client.OptimadeClient.callbacks]\n docstring for more details.\n verbosity: The verbosity level of the client.\n\n \"\"\"\n\n self.max_results_per_provider = max_results_per_provider\n if self.max_results_per_provider in (-1, 0):\n self.max_results_per_provider = None\n\n self._excluded_providers = set(exclude_providers) if exclude_providers else None\n self._included_providers = set(include_providers) if include_providers else None\n self._excluded_databases = set(exclude_databases) if exclude_databases else None\n\n self.max_attempts = max_attempts\n self.silent = silent\n self.verbosity = verbosity\n self.skip_ssl = skip_ssl\n\n if headers:\n self.headers.update(headers)\n\n if not base_urls:\n progress = None\n if not self.silent:\n progress = OptimadeClientProgress()\n self.base_urls = list(\n get_all_databases(\n exclude_providers=self._excluded_providers,\n include_providers=self._included_providers,\n exclude_databases=self._excluded_databases,\n progress=progress,\n skip_ssl=self.skip_ssl,\n )\n )\n else:\n if exclude_providers or include_providers or exclude_databases:\n raise RuntimeError(\n \"Cannot provide both a list of base URLs and included/excluded databases.\"\n )\n\n self.base_urls = base_urls\n\n if isinstance(self.base_urls, str):\n self.base_urls = [self.base_urls]\n self.base_urls = list(self.base_urls)\n\n if not self.base_urls:\n raise SystemExit(\n \"Unable to access any OPTIMADE base URLs. If you believe this is an error, try manually specifying some base URLs.\"\n )\n\n if http_timeout:\n if isinstance(http_timeout, httpx.Timeout):\n self.http_timeout = http_timeout\n else:\n self.http_timeout = httpx.Timeout(http_timeout)\n\n self.use_async = use_async\n\n if http_client:\n self._http_client = http_client\n if issubclass(self._http_client, httpx.AsyncClient):\n if not self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use synchronous mode with an asynchronous HTTP client, please set `use_async=True` or pass an asynchronous HTTP client.\"\n )\n self.use_async = True\n elif issubclass(self._http_client, requests.Session):\n if self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use async mode with a synchronous HTTP client, please set `use_async=False` or pass an synchronous HTTP client.\"\n )\n self.use_async = False\n else:\n if use_async:\n self._http_client = httpx.AsyncClient\n else:\n self._http_client = requests.Session\n\n self.callbacks = callbacks\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.binary_search_count","title":"binary_search_count(filter, endpoint, base_url, results=None)
","text":"In cases where data_returned
is not available (due to database limitations or otherwise), iteratively probe the final page of results available for a filter using binary search.
Note: These queries always happen synchronously across APIs, but can be executed asynchronously within a single API.
Parameters:
Name Type Description Defaultfilter
str
The OPTIMADE filter string for the query.
requiredendpoint
str
The endpoint to query.
requiredbase_url
str
The base URL to query.
requiredresults
Optional[dict]
The results from a previous query for the first page of results.
None
Returns:
Type Descriptionint
The number of results for the filter.
Source code inoptimade/client/client.py
def binary_search_count(\n self, filter: str, endpoint: str, base_url: str, results: Optional[dict] = None\n) -> int:\n \"\"\"In cases where `data_returned` is not available (due to database limitations or\n otherwise), iteratively probe the final page of results available for a filter using\n binary search.\n\n Note: These queries always happen synchronously across APIs, but can be executed\n asynchronously within a single API.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n results: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if self.verbosity:\n self._progress.print(f\"Performing binary search count for {base_url}\")\n if self.use_async:\n return self._binary_search_count_async(filter, endpoint, base_url, results)\n\n else:\n raise NotImplementedError(\n \"Binary search count is not yet implemented for synchronous queries.\"\n )\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.count","title":"count(filter=None, endpoint=None)
","text":"Counts the number of results for the filter, requiring only 1 request per provider by making use of the meta->data_returned
key. If missing, attempts will be made to perform an exponential/binary search over pagination to count the results.
Raises:
Type DescriptionRuntimeError
If the query could not be executed.
Returns:
Type Descriptiondict[str, dict[str, dict[str, Optional[int]]]]
A nested mapping from endpoint, filter and base URL to the number of query results.
Source code inoptimade/client/client.py
def count(\n self, filter: Optional[str] = None, endpoint: Optional[str] = None\n) -> dict[str, dict[str, dict[str, Optional[int]]]]:\n \"\"\"Counts the number of results for the filter, requiring\n only 1 request per provider by making use of the `meta->data_returned`\n key. If missing, attempts will be made to perform an exponential/binary\n search over pagination to count the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the number of query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Counting results for [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n page_limit=1,\n paginate=False,\n response_fields=[],\n sort=None,\n )\n count_results = {}\n\n for base_url in results:\n count_results[base_url] = results[base_url].meta.get(\n \"data_returned\", None\n )\n\n if count_results[base_url] is None or self._force_binary_search:\n if self.count_binary_search:\n count_results[base_url] = self.binary_search_count(\n filter, endpoint, base_url, results\n )\n else:\n self._progress.print(\n f\"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results. Full response: {results[base_url]}\"\n )\n\n self.count_results[endpoint][filter] = count_results\n return {endpoint: {filter: count_results}}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get","title":"get(filter=None, endpoint=None, response_fields=None, sort=None)
","text":"Gets the results from the endpoint and filter across the defined OPTIMADE APIs.
Parameters:
Name Type Description Defaultfilter
Optional[str]
The OPTIMADE filter string for the query.
None
endpoint
Optional[str]
The endpoint to query.
None
response_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
Raises:
Type DescriptionRuntimeError
If the query could not be executed.
Returns:
Type Descriptiondict[str, dict[str, dict[str, dict]]]
A nested mapping from endpoint, filter and base URL to the query results.
Source code inoptimade/client/client.py
def get(\n self,\n filter: Optional[str] = None,\n endpoint: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n) -> dict[str, dict[str, dict[str, dict]]]:\n \"\"\"Gets the results from the endpoint and filter across the\n defined OPTIMADE APIs.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Performing query [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n response_fields=response_fields,\n page_limit=None,\n paginate=True,\n sort=sort,\n )\n self.all_results[endpoint][filter] = results\n return {endpoint: {filter: {k: results[k].asdict() for k in results}}}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get_one","title":"get_one(endpoint, filter, base_url, response_fields=None, sort=None, page_limit=None, paginate=True, other_params=None)
","text":"Executes the query synchronously on one API.
Parameters:
Name Type Description Defaultendpoint
str
The OPTIMADE endpoint to query.
requiredfilter
str
The OPTIMADE filter string.
requiredresponse_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
page_limit
Optional[int]
A page limit to enforce for each query (used in conjunction with paginate
).
None
paginate
bool
Whether to pull all pages of results (up to the value of max_results_per_provider
) or whether to return after one page.
True
other_params
Optional[dict[str, Any]]
Any other parameters to pass to the server.
None
Returns:
Type Descriptiondict[str, QueryResults]
A dictionary mapping from base URL to the results of the query.
Source code inoptimade/client/client.py
def get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously on one API.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return self._get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]\"\n )\n return {base_url: error_query_results}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get_one_async","title":"get_one_async(endpoint, filter, base_url, response_fields=None, sort=None, page_limit=None, paginate=True, other_params=None)
async
","text":"Executes the query asynchronously on one API.
Note
This method currently makes non-blocking requests to a single API, but these requests are executed serially on that API, i.e., results are pulled one page at a time, but requests will not block other async requests to other APIs.
Parameters:
Name Type Description Defaultendpoint
str
The OPTIMADE endpoint to query.
requiredfilter
str
The OPTIMADE filter string.
requiredresponse_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
page_limit
Optional[int]
A page limit to enforce for each query (used in conjunction with paginate
).
None
paginate
bool
Whether to pull all pages of results (up to the value of max_results_per_provider
) or whether to return after one page.
True
other_params
Optional[dict[str, Any]]
Any other parameters to pass to the server.
None
Returns:
Type Descriptiondict[str, QueryResults]
A dictionary mapping from base URL to the results of the query.
Source code inoptimade/client/client.py
async def get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously on one API.\n\n !!! note\n This method currently makes non-blocking requests\n to a single API, but these requests are executed\n serially on that API, i.e., results are pulled one\n page at a time, but requests will not block other\n async requests to other APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return await self._get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{error_query_results.errors}[/red i]\"\n )\n return {base_url: error_query_results}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.list_properties","title":"list_properties(entry_type)
","text":"Returns the list of properties reported at /info/<entry_type>
for the given entry type, for each database.
optimade/client/client.py
def list_properties(\n self,\n entry_type: str,\n) -> dict[str, list[str]]:\n \"\"\"Returns the list of properties reported at `/info/<entry_type>`\n for the given entry type, for each database.\n\n \"\"\"\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Listing properties for [bold yellow]{entry_type}[/bold yellow]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n \"\",\n f\"info/{entry_type}\",\n paginate=False,\n page_limit=1,\n response_fields=[],\n sort=None,\n )\n self.property_lists = {entry_type: {}}\n for database in results:\n self.property_lists[entry_type][database] = list(\n results[database].data.get(\"properties\", {}).keys() # type: ignore\n )\n return self.property_lists[entry_type]\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.search_property","title":"search_property(query, entry_type)
","text":"Searches for the query substring within the listed properties served by each database.
Parameters:
Name Type Description Defaultquery
str
The substring to search for.
requiredentry_type
str
The entry type to query.
requiredReturns:
Type Descriptiondict[str, list[str]]
A nested dictionary of matching property lists, arranged by
dict[str, list[str]]
entry type and database.
Source code inoptimade/client/client.py
def search_property(self, query: str, entry_type: str) -> dict[str, list[str]]:\n \"\"\"Searches for the query substring within the listed properties\n served by each database.\n\n Parameters:\n query: The substring to search for.\n entry_type: The entry type to query.\n\n Returns:\n A nested dictionary of matching property lists, arranged by\n entry type and database.\n\n \"\"\"\n if not self.property_lists:\n self.list_properties(entry_type=entry_type)\n\n matching_properties: dict[str, dict[str, list[str]]] = {\n entry_type: defaultdict(list)\n }\n if entry_type in self.property_lists:\n for database in self.property_lists[entry_type]:\n for property in self.property_lists[entry_type][database]:\n if query in property:\n matching_properties[entry_type][database].append(property)\n return matching_properties[entry_type]\n
"},{"location":"api_reference/client/utils/","title":"utils","text":""},{"location":"api_reference/client/utils/#optimade.client.utils.OptimadeClientProgress","title":"OptimadeClientProgress
","text":" Bases: Progress
A wrapper around Rich.Progress
that defines the OPTIMADE client progressbars.
optimade/client/utils.py
class OptimadeClientProgress(Progress):\n \"\"\"A wrapper around `Rich.Progress` that defines the OPTIMADE client progressbars.\"\"\"\n\n def __init__(self):\n super().__init__(\n SpinnerColumn(finished_text=\"[green]\u2713\"),\n TextColumn(\"[progress.description]{task.description}\"),\n BarColumn(),\n TaskProgressColumn(\n text_format=\"[progress.completed]{task.completed}/[progress.total]{task.total}\",\n text_format_no_percentage=\"[progress.completed]{task.completed}\",\n ),\n TimeElapsedColumn(),\n console=Console(stderr=True),\n auto_refresh=True,\n refresh_per_second=10,\n )\n\n def print(self, *args, **kwargs):\n if not self.disable:\n super().print(*args, **kwargs)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.QueryResults","title":"QueryResults
dataclass
","text":"A container dataclass for the results from a given query.
Source code inoptimade/client/utils.py
@dataclass\nclass QueryResults:\n \"\"\"A container dataclass for the results from a given query.\"\"\"\n\n data: Union[dict, list[dict]] = field(default_factory=list, init=False) # type: ignore[assignment]\n errors: list[str] = field(default_factory=list, init=False)\n links: dict = field(default_factory=dict, init=False)\n included: list[dict] = field(default_factory=list, init=False)\n meta: dict = field(default_factory=dict, init=False)\n\n @property\n def included_index(self) -> set[str]:\n if not getattr(self, \"_included_index\", None):\n self._included_index: set[str] = set()\n return self._included_index\n\n def asdict(self):\n return asdict(self)\n\n def update(self, page_results: dict) -> None:\n \"\"\"Combine the results from one page with the existing results for a given query.\n\n Parameters:\n page_results: The results for the current page.\n\n \"\"\"\n\n if \"data\" in page_results:\n # If the `data` field is a list, add it to our existing results.\n # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)\n # and should be added as the only `data` field for these results.\n if isinstance(page_results[\"data\"], list):\n self.data.extend(page_results[\"data\"]) # type: ignore[union-attr]\n elif not self.data:\n self.data = page_results[\"data\"]\n else:\n raise RuntimeError(\n \"Not overwriting old `data` field in `QueryResults`.\"\n )\n\n if \"errors\" in page_results:\n self.errors.extend(page_results[\"errors\"])\n\n # Combine meta/links fields across all pages in a sensible way, i.e.,\n # if we really reached the last page of results, then make sure `links->next`\n # is null in the final response, and make sure `meta->more_data_available` is None or False.\n keys_to_filter = {\n \"links\": (\"next\", \"prev\"),\n \"meta\": (\"query\", \"more_data_available\"),\n }\n for top_level_key in keys_to_filter:\n if top_level_key not in page_results:\n page_results[top_level_key] = {}\n for k in keys_to_filter[top_level_key]:\n if k not in page_results[top_level_key]:\n page_results[top_level_key][k] = None\n getattr(self, top_level_key).update(\n {k: page_results[top_level_key][k] for k in page_results[top_level_key]}\n )\n\n # Only add new unique entries to the included list\n for d in page_results.get(\"included\", []):\n typed_id = f\"{d['type']}/{d['id']}\"\n if typed_id not in self.included_index:\n self.included_index.add(typed_id)\n self.included.append(d)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.QueryResults.update","title":"update(page_results)
","text":"Combine the results from one page with the existing results for a given query.
Parameters:
Name Type Description Defaultpage_results
dict
The results for the current page.
required Source code inoptimade/client/utils.py
def update(self, page_results: dict) -> None:\n \"\"\"Combine the results from one page with the existing results for a given query.\n\n Parameters:\n page_results: The results for the current page.\n\n \"\"\"\n\n if \"data\" in page_results:\n # If the `data` field is a list, add it to our existing results.\n # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)\n # and should be added as the only `data` field for these results.\n if isinstance(page_results[\"data\"], list):\n self.data.extend(page_results[\"data\"]) # type: ignore[union-attr]\n elif not self.data:\n self.data = page_results[\"data\"]\n else:\n raise RuntimeError(\n \"Not overwriting old `data` field in `QueryResults`.\"\n )\n\n if \"errors\" in page_results:\n self.errors.extend(page_results[\"errors\"])\n\n # Combine meta/links fields across all pages in a sensible way, i.e.,\n # if we really reached the last page of results, then make sure `links->next`\n # is null in the final response, and make sure `meta->more_data_available` is None or False.\n keys_to_filter = {\n \"links\": (\"next\", \"prev\"),\n \"meta\": (\"query\", \"more_data_available\"),\n }\n for top_level_key in keys_to_filter:\n if top_level_key not in page_results:\n page_results[top_level_key] = {}\n for k in keys_to_filter[top_level_key]:\n if k not in page_results[top_level_key]:\n page_results[top_level_key][k] = None\n getattr(self, top_level_key).update(\n {k: page_results[top_level_key][k] for k in page_results[top_level_key]}\n )\n\n # Only add new unique entries to the included list\n for d in page_results.get(\"included\", []):\n typed_id = f\"{d['type']}/{d['id']}\"\n if typed_id not in self.included_index:\n self.included_index.add(typed_id)\n self.included.append(d)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.RecoverableHTTPError","title":"RecoverableHTTPError
","text":" Bases: Exception
Base class for any HTTP issues that may be recoverable by just repeating the query.
Source code inoptimade/client/utils.py
class RecoverableHTTPError(Exception):\n \"\"\"Base class for any HTTP issues that may be recoverable by just\n repeating the query.\"\"\"\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.TooManyRequestsException","title":"TooManyRequestsException
","text":" Bases: RecoverableHTTPError
For when the underlying HTTP request returns 429: Too Many Requests.
Source code inoptimade/client/utils.py
class TooManyRequestsException(RecoverableHTTPError):\n \"\"\"For when the underlying HTTP request returns 429: Too Many Requests.\"\"\"\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.silent_raise","title":"silent_raise()
","text":"Raise an exception without printing a traceback, or the exception message itself.
Source code inoptimade/client/utils.py
@contextmanager\ndef silent_raise():\n \"\"\"Raise an exception without printing a traceback, or the exception message itself.\"\"\"\n default_value = getattr(\n sys, \"tracebacklimit\", 1000\n ) # `1000` is a Python's default value\n default_excepthook = getattr(sys, \"excepthook\")\n sys.tracebacklimit = 0\n sys.excepthook = lambda type, value, traceback: None\n yield\n sys.tracebacklimit = default_value # revert changes\n sys.excepthook = default_excepthook\n
"},{"location":"api_reference/filterparser/lark_parser/","title":"lark_parser","text":"This submodule implements the LarkParser
class, which uses the lark library to parse filter strings with a defined OPTIMADE filter grammar into Lark.Tree
objects for use by the filter transformers.
LarkParser
","text":"This class wraps a versioned OPTIMADE grammar and allows it to be parsed into Lark tree objects.
Source code inoptimade/filterparser/lark_parser.py
class LarkParser:\n \"\"\"This class wraps a versioned OPTIMADE grammar and allows\n it to be parsed into Lark tree objects.\n\n \"\"\"\n\n def __init__(\n self, version: Optional[tuple[int, int, int]] = None, variant: str = \"default\"\n ):\n \"\"\"For a given version and variant, try to load the corresponding grammar.\n\n Parameters:\n version: The grammar version number to use (e.g., `(1, 0, 1)` for v1.0.1).\n variant: The grammar variant to employ.\n\n Raises:\n ParserError: If the requested version/variant of the\n grammar does not exist.\n\n \"\"\"\n\n if not version:\n version = max(\n _ for _ in AVAILABLE_PARSERS if AVAILABLE_PARSERS[_].get(\"default\")\n )\n\n if version not in AVAILABLE_PARSERS:\n raise ParserError(f\"Unknown parser grammar version: {version}\")\n\n if variant not in AVAILABLE_PARSERS[version]:\n raise ParserError(f\"Unknown variant of the parser: {variant}\")\n\n self.version = version\n self.variant = variant\n\n with open(AVAILABLE_PARSERS[version][variant]) as f:\n self.lark = Lark(f, maybe_placeholders=False)\n\n self.tree: Optional[Tree] = None\n self.filter: Optional[str] = None\n\n def parse(self, filter_: str) -> Tree:\n \"\"\"Parse a filter string into a `lark.Tree`.\n\n Parameters:\n filter_: The filter string to parse.\n\n Raises:\n BadRequest: If the filter cannot be parsed.\n\n Returns:\n The parsed filter.\n\n \"\"\"\n try:\n self.tree = self.lark.parse(filter_)\n self.filter = filter_\n return self.tree\n except Exception as exc:\n raise BadRequest(\n detail=f\"Unable to parse filter {filter_}. Lark traceback: \\n{exc}\"\n ) from exc\n\n def __repr__(self):\n if isinstance(self.tree, Tree):\n return self.tree.pretty()\n return repr(self.lark)\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.LarkParser.__init__","title":"__init__(version=None, variant='default')
","text":"For a given version and variant, try to load the corresponding grammar.
Parameters:
Name Type Description Defaultversion
Optional[tuple[int, int, int]]
The grammar version number to use (e.g., (1, 0, 1)
for v1.0.1).
None
variant
str
The grammar variant to employ.
'default'
Raises:
Type DescriptionParserError
If the requested version/variant of the grammar does not exist.
Source code inoptimade/filterparser/lark_parser.py
def __init__(\n self, version: Optional[tuple[int, int, int]] = None, variant: str = \"default\"\n):\n \"\"\"For a given version and variant, try to load the corresponding grammar.\n\n Parameters:\n version: The grammar version number to use (e.g., `(1, 0, 1)` for v1.0.1).\n variant: The grammar variant to employ.\n\n Raises:\n ParserError: If the requested version/variant of the\n grammar does not exist.\n\n \"\"\"\n\n if not version:\n version = max(\n _ for _ in AVAILABLE_PARSERS if AVAILABLE_PARSERS[_].get(\"default\")\n )\n\n if version not in AVAILABLE_PARSERS:\n raise ParserError(f\"Unknown parser grammar version: {version}\")\n\n if variant not in AVAILABLE_PARSERS[version]:\n raise ParserError(f\"Unknown variant of the parser: {variant}\")\n\n self.version = version\n self.variant = variant\n\n with open(AVAILABLE_PARSERS[version][variant]) as f:\n self.lark = Lark(f, maybe_placeholders=False)\n\n self.tree: Optional[Tree] = None\n self.filter: Optional[str] = None\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.LarkParser.parse","title":"parse(filter_)
","text":"Parse a filter string into a lark.Tree
.
Parameters:
Name Type Description Defaultfilter_
str
The filter string to parse.
requiredRaises:
Type DescriptionBadRequest
If the filter cannot be parsed.
Returns:
Type DescriptionTree
The parsed filter.
Source code inoptimade/filterparser/lark_parser.py
def parse(self, filter_: str) -> Tree:\n \"\"\"Parse a filter string into a `lark.Tree`.\n\n Parameters:\n filter_: The filter string to parse.\n\n Raises:\n BadRequest: If the filter cannot be parsed.\n\n Returns:\n The parsed filter.\n\n \"\"\"\n try:\n self.tree = self.lark.parse(filter_)\n self.filter = filter_\n return self.tree\n except Exception as exc:\n raise BadRequest(\n detail=f\"Unable to parse filter {filter_}. Lark traceback: \\n{exc}\"\n ) from exc\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.ParserError","title":"ParserError
","text":" Bases: Exception
Triggered by critical parsing errors that should lead to 500 Server Error HTTP statuses.
Source code inoptimade/filterparser/lark_parser.py
class ParserError(Exception):\n \"\"\"Triggered by critical parsing errors that should lead\n to 500 Server Error HTTP statuses.\n \"\"\"\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.get_versions","title":"get_versions()
","text":"Find grammar files within this package's grammar directory, returning a dictionary broken down by scraped grammar version (major, minor, patch) and variant (a string tag).
Returns:
Type Descriptiondict[tuple[int, int, int], dict[str, Path]]
A mapping from version, variant to grammar file name.
Source code inoptimade/filterparser/lark_parser.py
def get_versions() -> dict[tuple[int, int, int], dict[str, Path]]:\n \"\"\"Find grammar files within this package's grammar directory,\n returning a dictionary broken down by scraped grammar version\n (major, minor, patch) and variant (a string tag).\n\n Returns:\n A mapping from version, variant to grammar file name.\n\n \"\"\"\n dct: dict[tuple[int, int, int], dict[str, Path]] = {}\n for filename in Path(__file__).parent.joinpath(\"../grammar\").glob(\"*.lark\"):\n tags = filename.stem.lstrip(\"v\").split(\".\")\n version: tuple[int, int, int] = (int(tags[0]), int(tags[1]), int(tags[2]))\n variant: str = \"default\" if len(tags) == 3 else str(tags[-1])\n if version not in dct:\n dct[version] = {}\n dct[version][variant] = filename\n return dct\n
"},{"location":"api_reference/filtertransformers/base_transformer/","title":"base_transformer","text":"This submodule implements the BaseTransformer
and Quantity
classes for turning filters parsed by lark into backend-specific queries.
BaseTransformer
","text":" Bases: Transformer
, ABC
Generic filter transformer that handles various parts of the grammar in a backend non-specific way.
Attributes:
Name Type Descriptionoperator_map
dict[str, Optional[str]]
A map from comparison operators to their backend-specific versions.
mapper
Optional[type[BaseResourceMapper]]
A resource mapper object that defines the expected fields and acts as a container for various field-related configuration.
Source code inoptimade/filtertransformers/base_transformer.py
class BaseTransformer(Transformer, abc.ABC):\n \"\"\"Generic filter transformer that handles various\n parts of the grammar in a backend non-specific way.\n\n Attributes:\n operator_map: A map from comparison operators\n to their backend-specific versions.\n mapper: A resource mapper object that defines the\n expected fields and acts as a container for\n various field-related configuration.\n\n \"\"\"\n\n mapper: Optional[type[BaseResourceMapper]] = None\n operator_map: dict[str, Optional[str]] = {\n \"<\": None,\n \"<=\": None,\n \">\": None,\n \">=\": None,\n \"!=\": None,\n \"=\": None,\n }\n\n # map from operators to their syntactic (as opposed to logical) inverse to handle\n # equivalence between cases like \"A > 3\" and \"3 < A\".\n _reversed_operator_map = {\n \">\": \"<\",\n \">=\": \"<=\",\n \"<\": \">\",\n \"<=\": \">=\",\n \"=\": \"=\",\n \"!=\": \"!=\",\n }\n\n _quantity_type: type[Quantity] = Quantity\n _quantities = None\n\n def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n\n @property\n def backend_mapping(self) -> dict[str, Quantity]:\n \"\"\"A mapping between backend field names (aliases) and the corresponding\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] object.\n \"\"\"\n return {\n quantity.backend_field: quantity # type: ignore[misc]\n for _, quantity in self.quantities.items()\n }\n\n @property\n def quantities(self) -> dict[str, Quantity]:\n \"\"\"A mapping from the OPTIMADE field name to the corresponding\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects.\n \"\"\"\n if self._quantities is None:\n self._quantities = self._build_quantities()\n\n return self._quantities\n\n @quantities.setter\n def quantities(self, quantities: dict[str, Quantity]) -> None:\n self._quantities = quantities\n\n def _build_quantities(self) -> dict[str, Quantity]:\n \"\"\"Creates a dictionary of field names mapped to\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects from the\n fields registered by the mapper.\n\n \"\"\"\n\n quantities = {}\n\n if self.mapper is not None:\n for field in self.mapper.ALL_ATTRIBUTES:\n alias = self.mapper.get_backend_field(field)\n # Allow length aliases to be defined relative to either backend fields or OPTIMADE fields,\n # with preference for those defined from OPTIMADE fields\n length_alias = self.mapper.length_alias_for(\n field\n ) or self.mapper.length_alias_for(alias)\n\n if field not in quantities:\n quantities[field] = self._quantity_type(\n name=field, backend_field=alias\n )\n\n if length_alias:\n if length_alias not in quantities:\n quantities[length_alias] = self._quantity_type(\n name=length_alias,\n backend_field=self.mapper.get_backend_field(length_alias),\n )\n quantities[field].length_quantity = quantities[length_alias]\n\n return quantities\n\n def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n\n def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n\n def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n\n def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n\n @v_args(inline=True)\n def constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n\n def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n\n @v_args(inline=True)\n def string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n\n @v_args(inline=True)\n def signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n\n @v_args(inline=True)\n def number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n\n @v_args(inline=True)\n def comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n\n def value_list(self, arg):\n \"\"\"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\"\"\"\n\n def value_zip(self, arg):\n \"\"\"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\"\"\"\n pass\n\n def value_zip_list(self, arg):\n \"\"\"value_zip_list: value_zip ( \",\" value_zip )*\"\"\"\n\n def expression(self, arg):\n \"\"\"expression: expression_clause ( OR expression_clause )\"\"\"\n\n def expression_clause(self, arg):\n \"\"\"expression_clause: expression_phrase ( AND expression_phrase )*\"\"\"\n\n def expression_phrase(self, arg):\n \"\"\"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\"\"\"\n\n def property_first_comparison(self, arg):\n \"\"\"property_first_comparison:\n property ( value_op_rhs\n | known_op_rhs\n | fuzzy_string_op_rhs\n | set_op_rhs\n | set_zip_op_rhs\n | length_op_rhs )\n\n \"\"\"\n\n def constant_first_comparison(self, arg):\n \"\"\"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\"\"\"\n\n @v_args(inline=True)\n def value_op_rhs(self, operator, value):\n \"\"\"value_op_rhs: OPERATOR value\"\"\"\n\n def known_op_rhs(self, arg):\n \"\"\"known_op_rhs: IS ( KNOWN | UNKNOWN )\"\"\"\n\n def fuzzy_string_op_rhs(self, arg):\n \"\"\"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\"\"\"\n\n def set_op_rhs(self, arg):\n \"\"\"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\"\"\"\n\n def length_op_rhs(self, arg):\n \"\"\"length_op_rhs: LENGTH [ OPERATOR ] value\"\"\"\n\n def set_zip_op_rhs(self, arg):\n \"\"\"set_zip_op_rhs: property_zip_addon HAS ( value_zip\n | ONLY value_zip_list\n | ALL value_zip_list\n | ANY value_zip_list )\n\n \"\"\"\n\n def property_zip_addon(self, arg):\n \"\"\"property_zip_addon: \":\" property (\":\" property)*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
quantities: dict[str, Quantity]
property
writable
","text":"A mapping from the OPTIMADE field name to the corresponding Quantity
objects.
__default__(data, children, meta)
","text":"The default rule to call when no definition is found for a particular construct.
Source code inoptimade/filtertransformers/base_transformer.py
def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.__init__","title":"__init__(mapper=None)
","text":"Initialise the transformer object, optionally loading in a resource mapper for use when post-processing.
Source code inoptimade/filtertransformers/base_transformer.py
def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.constant_first_comparison","title":"constant_first_comparison(arg)
","text":"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )
Source code inoptimade/filtertransformers/base_transformer.py
def constant_first_comparison(self, arg):\n \"\"\"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression","title":"expression(arg)
","text":"expression: expression_clause ( OR expression_clause )
Source code inoptimade/filtertransformers/base_transformer.py
def expression(self, arg):\n \"\"\"expression: expression_clause ( OR expression_clause )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression_clause","title":"expression_clause(arg)
","text":"expression_clause: expression_phrase ( AND expression_phrase )*
Source code inoptimade/filtertransformers/base_transformer.py
def expression_clause(self, arg):\n \"\"\"expression_clause: expression_phrase ( AND expression_phrase )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression_phrase","title":"expression_phrase(arg)
","text":"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )
Source code inoptimade/filtertransformers/base_transformer.py
def expression_phrase(self, arg):\n \"\"\"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.filter","title":"filter(arg)
","text":"filter: expression*
Source code inoptimade/filtertransformers/base_transformer.py
def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.fuzzy_string_op_rhs","title":"fuzzy_string_op_rhs(arg)
","text":"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value
Source code inoptimade/filtertransformers/base_transformer.py
def fuzzy_string_op_rhs(self, arg):\n \"\"\"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.known_op_rhs","title":"known_op_rhs(arg)
","text":"known_op_rhs: IS ( KNOWN | UNKNOWN )
Source code inoptimade/filtertransformers/base_transformer.py
def known_op_rhs(self, arg):\n \"\"\"known_op_rhs: IS ( KNOWN | UNKNOWN )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.length_op_rhs","title":"length_op_rhs(arg)
","text":"length_op_rhs: LENGTH [ OPERATOR ] value
Source code inoptimade/filtertransformers/base_transformer.py
def length_op_rhs(self, arg):\n \"\"\"length_op_rhs: LENGTH [ OPERATOR ] value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.number","title":"number(number)
","text":"number: SIGNED_INT | SIGNED_FLOAT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.postprocess","title":"postprocess(query)
","text":"Post-process the query according to the rules defined for the backend, returning the backend-specific query.
Source code inoptimade/filtertransformers/base_transformer.py
def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property","title":"property(args)
","text":"property: IDENTIFIER ( \".\" IDENTIFIER )*
If this transformer has an associated mapper, the property will be compared to possible relationship entry types and for any supported provider prefixes. If there is a match, this rule will return a string and not a dereferenced Quantity
.
Raises:
Type DescriptionBadRequest
If the property does not match any of the above rules.
Source code inoptimade/filtertransformers/base_transformer.py
def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property_first_comparison","title":"property_first_comparison(arg)
","text":"property_first_comparison: property ( value_op_rhs | known_op_rhs | fuzzy_string_op_rhs | set_op_rhs | set_zip_op_rhs | length_op_rhs )
Source code inoptimade/filtertransformers/base_transformer.py
def property_first_comparison(self, arg):\n \"\"\"property_first_comparison:\n property ( value_op_rhs\n | known_op_rhs\n | fuzzy_string_op_rhs\n | set_op_rhs\n | set_zip_op_rhs\n | length_op_rhs )\n\n \"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property_zip_addon","title":"property_zip_addon(arg)
","text":"property_zip_addon: \":\" property (\":\" property)*
Source code inoptimade/filtertransformers/base_transformer.py
def property_zip_addon(self, arg):\n \"\"\"property_zip_addon: \":\" property (\":\" property)*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.set_op_rhs","title":"set_op_rhs(arg)
","text":"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )
Source code inoptimade/filtertransformers/base_transformer.py
def set_op_rhs(self, arg):\n \"\"\"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.set_zip_op_rhs","title":"set_zip_op_rhs(arg)
","text":"set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )
Source code inoptimade/filtertransformers/base_transformer.py
def set_zip_op_rhs(self, arg):\n \"\"\"set_zip_op_rhs: property_zip_addon HAS ( value_zip\n | ONLY value_zip_list\n | ALL value_zip_list\n | ANY value_zip_list )\n\n \"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.signed_int","title":"signed_int(number)
","text":"signed_int : SIGNED_INT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.string","title":"string(string)
","text":"string: ESCAPED_STRING
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_list","title":"value_list(arg)
","text":"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*
Source code inoptimade/filtertransformers/base_transformer.py
def value_list(self, arg):\n \"\"\"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_op_rhs","title":"value_op_rhs(operator, value)
","text":"value_op_rhs: OPERATOR value
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value_op_rhs(self, operator, value):\n \"\"\"value_op_rhs: OPERATOR value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_zip","title":"value_zip(arg)
","text":"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*
Source code inoptimade/filtertransformers/base_transformer.py
def value_zip(self, arg):\n \"\"\"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\"\"\"\n pass\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_zip_list","title":"value_zip_list(arg)
","text":"value_zip_list: value_zip ( \",\" value_zip )*
Source code inoptimade/filtertransformers/base_transformer.py
def value_zip_list(self, arg):\n \"\"\"value_zip_list: value_zip ( \",\" value_zip )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.Quantity","title":"Quantity
","text":"Class to provide information about available quantities to the transformer.
The transformer can use Quantity
's to
Attributes:
Name Type Descriptionname
str
The name of the quantity as used in the filter expressions.
backend_field
Optional[str]
The name of the field for this quantity in the backend database, will be name
by default.
length_quantity
Optional[Quantity]
Another (typically integer) Quantity
that can be queried as the length of this quantity, e.g. elements
and nelements
. Backends can then decide whether to use this for all \"LENGTH\" queries.
optimade/filtertransformers/base_transformer.py
class Quantity:\n \"\"\"Class to provide information about available quantities to the transformer.\n\n The transformer can use [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]'s to\n\n * do some semantic checks,\n * map quantities to the underlying backend field name.\n\n Attributes:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n name: str\n backend_field: Optional[str]\n length_quantity: Optional[\"Quantity\"]\n\n def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"Quantity\"] = None,\n ):\n \"\"\"Initialise the `quantity` from it's name and aliases.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n self.name = name\n self.backend_field = backend_field if backend_field is not None else name\n self.length_quantity = length_quantity\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.Quantity.__init__","title":"__init__(name, backend_field=None, length_quantity=None)
","text":"Initialise the quantity
from it's name and aliases.
Parameters:
Name Type Description Defaultname
str
The name of the quantity as used in the filter expressions.
requiredbackend_field
Optional[str]
The name of the field for this quantity in the backend database, will be name
by default.
None
length_quantity
Optional[Quantity]
Another (typically integer) Quantity
that can be queried as the length of this quantity, e.g. elements
and nelements
. Backends can then decide whether to use this for all \"LENGTH\" queries.
None
Source code in optimade/filtertransformers/base_transformer.py
def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"Quantity\"] = None,\n):\n \"\"\"Initialise the `quantity` from it's name and aliases.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n self.name = name\n self.backend_field = backend_field if backend_field is not None else name\n self.length_quantity = length_quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/","title":"elasticsearch","text":""},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer","title":"ElasticTransformer
","text":" Bases: BaseTransformer
Transformer that transforms v0.10.1
/v1.0
grammar parse trees into Elasticsearch queries.
Uses elasticsearch_dsl and will produce an elasticsearch_dsl.Q
instance.
optimade/filtertransformers/elasticsearch.py
class ElasticTransformer(BaseTransformer):\n \"\"\"Transformer that transforms ``v0.10.1``/`v1.0` grammar parse\n trees into Elasticsearch queries.\n\n Uses elasticsearch_dsl and will produce an `elasticsearch_dsl.Q` instance.\n\n \"\"\"\n\n operator_map = {\n \"<\": \"lt\",\n \"<=\": \"lte\",\n \">\": \"gt\",\n \">=\": \"gte\",\n }\n\n _quantity_type: type[ElasticsearchQuantity] = ElasticsearchQuantity\n\n def __init__(\n self,\n mapper: type[BaseResourceMapper],\n quantities: Optional[dict[str, Quantity]] = None,\n ):\n if quantities is not None:\n self.quantities = quantities\n\n super().__init__(mapper=mapper)\n\n def _field(\n self, quantity: Union[str, Quantity], nested: Optional[Quantity] = None\n ) -> str:\n \"\"\"Used to unwrap from `property` to the string backend field name.\n\n If passed a `Quantity` (or a derived `ElasticsearchQuantity`), this method\n returns the backend field name, modulo some handling of nested fields.\n\n If passed a string quantity name:\n - Check that the name does not match a relationship type,\n raising a `NotImplementedError` if it does.\n - If the string is prefixed by an underscore, assume this is a\n provider-specific field from another provider and simply return it.\n The original `property` rule would have already filtered out provider\n fields for this backend appropriately as `Quantity` objects.\n\n Returns:\n The field name to use for database queries.\n\n \"\"\"\n\n if isinstance(quantity, str):\n if quantity in self.mapper.RELATIONSHIP_ENTRY_TYPES: # type: ignore[union-attr]\n raise NotImplementedError(\n f\"Unable to filter on relationships with type {quantity!r}\"\n )\n\n # In this case, the property rule has already filtered out fields\n # that do not match this provider, so this indicates an \"other provider\"\n # field that should be passed over\n if quantity.startswith(\"_\"):\n return quantity\n\n if nested is not None:\n return f\"{nested.backend_field}.{quantity.name}\" # type: ignore[union-attr]\n\n return quantity.backend_field # type: ignore[union-attr, return-value]\n\n def _query_op(\n self,\n quantity: Union[ElasticsearchQuantity, str],\n op: str,\n value: Union[str, float, int],\n nested: Optional[ElasticsearchQuantity] = None,\n ) -> Q:\n \"\"\"Return a range, match, or term query for the given quantity, comparison\n operator, and value.\n\n Returns:\n An elasticsearch_dsl query.\n\n Raises:\n BadRequest: If the query is not well-defined or is not supported.\n \"\"\"\n field = self._field(quantity, nested=nested)\n if op in self.operator_map:\n return Q(\"range\", **{field: {self.operator_map[op]: value}})\n\n # If quantity is an \"other provider\" field then use Keyword as the default\n # mapping type. These queries should not match on anything as the field\n # is not present in the index.\n elastic_mapping_type = Keyword\n if isinstance(quantity, ElasticsearchQuantity):\n elastic_mapping_type = quantity.elastic_mapping_type\n\n if elastic_mapping_type == Text:\n query_type = \"match\"\n elif elastic_mapping_type in [Keyword, Integer]:\n query_type = \"term\"\n else:\n raise NotImplementedError(\"Quantity has unsupported ES field type\")\n\n if op in [\"=\", \"\"]:\n return Q(query_type, **{field: value})\n\n if op == \"!=\":\n # != queries must also include an existence check\n # Note that for MongoDB, `$exists` will include null-valued fields,\n # where as in ES `exists` excludes them.\n\n return ~Q(query_type, **{field: value}) & Q(\"exists\", field=field)\n\n def _has_query_op(self, quantities, op, predicate_zip_list):\n \"\"\"Returns a bool query that combines the operator calls `_query_op`\n for each predicate and zipped quantity predicate combination.\n \"\"\"\n if op == \"HAS\":\n kind = \"must\" # in case of HAS we do a must over the \"list\" of the one given element\n elif op == \"HAS ALL\":\n kind = \"must\"\n elif op == \"HAS ANY\":\n kind = \"should\"\n elif op == \"HAS ONLY\":\n # HAS ONLY comes with heavy limitations, because there is no such thing\n # in elastic search. Only supported for elements, where we can construct\n # an anonymous \"formula\" based on elements sorted by order number and\n # can do a = comparision to check if all elements are contained\n\n # @ml-evs: Disabling this HAS ONLY workaround as tests are not passing\n raise NotImplementedError(\n \"HAS ONLY queries are not currently supported by the Elasticsearch backend.\"\n )\n\n # from optimade.models import CHEMICAL_SYMBOLS, ATOMIC_NUMBERS\n\n # if len(quantities) > 1:\n # raise NotImplementedError(\"HAS ONLY is not supported with zip\")\n # quantity = quantities[0]\n\n # if quantity.has_only_quantity is None:\n # raise NotImplementedError(\n # \"HAS ONLY is not supported by %s\" % quantity.name\n # )\n\n # def values():\n # for predicates in predicate_zip_list:\n # if len(predicates) != 1:\n # raise NotImplementedError(\"Tuples not supported in HAS ONLY\")\n # op, value = predicates[0]\n # if op != \"=\":\n # raise NotImplementedError(\n # \"Predicated not supported in HAS ONLY\"\n # )\n # if not isinstance(value, str):\n # raise NotImplementedError(\"Only strings supported in HAS ONLY\")\n # yield value\n\n # try:\n # order_numbers = list([ATOMIC_NUMBERS[element] for element in values()])\n # order_numbers.sort()\n # value = \"\".join(\n # [CHEMICAL_SYMBOLS[number - 1] for number in order_numbers]\n # )\n # except KeyError:\n # raise NotImplementedError(\n # \"HAS ONLY is only supported for chemical symbols\"\n # )\n\n # return Q(\"term\", **{quantity.has_only_quantity.name: value})\n else:\n raise NotImplementedError(f\"Unrecognised operation {op}.\")\n\n queries = [\n self._has_query(quantities, predicates) for predicates in predicate_zip_list\n ]\n return Q(\"bool\", **{kind: queries})\n\n def _has_query(self, quantities, predicates):\n \"\"\"\n Returns a bool query that combines the operator queries ():func:`_query_op`)\n for quantity pericate combination.\n \"\"\"\n if len(quantities) != len(predicates):\n raise ValueError(\n \"Tuple length does not match: %s <o> %s \"\n % (\":\".join(quantities), \":\".join(predicates))\n )\n\n if len(quantities) == 1:\n o, value = predicates[0]\n return self._query_op(quantities[0], o, value)\n\n nested_quantity = quantities[0].nested_quantity\n same_nested_quantity = any(\n q.nested_quantity != nested_quantity for q in quantities\n )\n if nested_quantity is None or same_nested_quantity:\n raise NotImplementedError(\n \"Expression with tuples are only supported for %s\"\n % \", \".join(quantities)\n )\n\n queries = [\n self._query_op(quantity, o, value, nested=nested_quantity)\n for quantity, (o, value) in zip(quantities, predicates)\n ]\n\n return Q(\n \"nested\",\n path=self._field(nested_quantity),\n query=dict(bool=dict(must=queries)),\n )\n\n def __default__(self, data: Any, children: Any, meta: Any) -> Any:\n \"\"\"Default behavior for rules that only replace one symbol with another\"\"\"\n return children[0]\n\n def filter(self, args):\n # filter: expression*\n if len(args) == 1:\n return args[0]\n return Q(\"bool\", **{\"must\": args})\n\n def expression_clause(self, args):\n # expression_clause: expression_phrase ( _AND expression_phrase )*\n result = args[0]\n for arg in args[1:]:\n result &= arg\n return result\n\n def expression(self, args):\n # expression: expression_clause ( _OR expression_clause )*\n result = args[0]\n for arg in args[1:]:\n result |= arg\n return result\n\n def expression_phrase(self, args):\n # expression_phrase: [ NOT ] ( operator | \"(\" expression \")\" )\n if args[0] == \"NOT\":\n return ~args[1]\n return args[0]\n\n @v_args(inline=True)\n def property_first_comparison(self, quantity, query):\n # property_first_comparison: property *_rhs\n return query(quantity)\n\n @v_args(inline=True)\n def constant_first_comparison(self, value, op, quantity):\n # constant_first_comparison: constant OPERATOR ( non_string_value | ...not_implemented_string )\n if not isinstance(quantity, ElasticsearchQuantity):\n raise TypeError(\"Only quantities can be compared to constant values.\")\n\n return self._query_op(quantity, self._reversed_operator_map[op], value)\n\n @v_args(inline=True)\n def value_op_rhs(self, op, value):\n # value_op_rhs: OPERATOR value\n return lambda quantity: self._query_op(quantity, op, value)\n\n def length_op_rhs(self, args):\n # length_op_rhs: LENGTH [ OPERATOR ] signed_int\n value = args[-1]\n if len(args) == 3:\n op = args[1]\n else:\n op = \"=\"\n\n def query(quantity):\n # This is only the case if quantity is an \"other\" provider's field,\n # in which case, we should treat it as unknown and try to do a null query\n if isinstance(quantity, str):\n return self._query_op(quantity, op, value)\n\n if quantity.length_quantity is None:\n raise NotImplementedError(\n f\"LENGTH is not supported for {quantity.name!r}\"\n )\n quantity = quantity.length_quantity\n return self._query_op(quantity, op, value)\n\n return query\n\n @v_args(inline=True)\n def known_op_rhs(self, _, value):\n # known_op_rhs: IS ( KNOWN | UNKNOWN )\n\n def query(quantity):\n query = Q(\"exists\", field=self._field(quantity))\n if value == \"KNOWN\":\n return query\n elif value == \"UNKNOWN\":\n return ~query\n raise NotImplementedError\n\n return query\n\n def set_op_rhs(self, args):\n # set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ... )\n values = args[-1]\n if not isinstance(values, list):\n if len(args) == 3:\n op = args[1]\n else:\n op = \"=\"\n values = [(op, values)]\n\n if len(args) == 3:\n op = \"HAS \" + args[1]\n else:\n op = \"HAS\"\n\n return lambda quantity: self._has_query_op(\n [quantity], op, [[value] for value in values]\n )\n\n def set_zip_op_rhs(self, args):\n # set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )\n add_on = args[0]\n values = args[-1]\n if len(args) == 4:\n op = \"HAS \" + args[2]\n else:\n op = \"HAS\"\n values = [values]\n\n return lambda quantity: self._has_query_op([quantity] + add_on, op, values)\n\n def property_zip_addon(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return args\n\n def value_zip(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return self.value_list(args)\n\n def value_zip_list(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return args\n\n def value_list(self, args):\n result = []\n op = \"=\"\n for arg in args:\n if arg in [\"<\", \"<=\", \">\", \">=\", \"!=\", \"=\"]:\n op = arg\n else:\n result.append(\n (\n op,\n arg,\n )\n )\n op = \"=\"\n return result\n\n def fuzzy_string_op_rhs(self, args):\n op = args[0]\n value = args[-1]\n if op == \"CONTAINS\":\n wildcard = \"*%s*\" % value\n if op == \"STARTS\":\n wildcard = \"%s*\" % value\n if op == \"ENDS\":\n wildcard = \"*%s\" % value\n\n return lambda quantity: Q(\"wildcard\", **{self._field(quantity): wildcard})\n\n @v_args(inline=True)\n def string(self, string):\n # string: ESCAPED_STRING\n return string.strip('\"')\n\n @v_args(inline=True)\n def signed_int(self, number):\n # signed_int : SIGNED_INT\n return int(number)\n\n @v_args(inline=True)\n def number(self, number):\n # number: SIGNED_INT | SIGNED_FLOAT\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
__default__(data, children, meta)
","text":"Default behavior for rules that only replace one symbol with another
Source code inoptimade/filtertransformers/elasticsearch.py
def __default__(self, data: Any, children: Any, meta: Any) -> Any:\n \"\"\"Default behavior for rules that only replace one symbol with another\"\"\"\n return children[0]\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.postprocess","title":"postprocess(query)
","text":"Post-process the query according to the rules defined for the backend, returning the backend-specific query.
Source code inoptimade/filtertransformers/base_transformer.py
def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.property","title":"property(args)
","text":"property: IDENTIFIER ( \".\" IDENTIFIER )*
If this transformer has an associated mapper, the property will be compared to possible relationship entry types and for any supported provider prefixes. If there is a match, this rule will return a string and not a dereferenced Quantity
.
Raises:
Type DescriptionBadRequest
If the property does not match any of the above rules.
Source code inoptimade/filtertransformers/base_transformer.py
def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticsearchQuantity","title":"ElasticsearchQuantity
","text":" Bases: Quantity
Elasticsearch-specific extension of the underlying Quantity
class.
Attributes:
Name Type Descriptionname
str
The name of the quantity as used in the filter expressions.
backend_field
Optional[str]
The name of the field for this quantity in Elasticsearch, will be name
by default.
elastic_mapping_type
Optional[Field]
A decendent of an elasticsearch_dsl.Field
that denotes which mapping type was used in the Elasticsearch index.
length_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.
has_only_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS
) and quantities with this attribute.
nested_quantity
Optional[ElasticsearchQuantity]
To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.
Source code inoptimade/filtertransformers/elasticsearch.py
class ElasticsearchQuantity(Quantity):\n \"\"\"Elasticsearch-specific extension of the underlying\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] class.\n\n Attributes:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n name: str\n backend_field: Optional[str]\n length_quantity: Optional[\"ElasticsearchQuantity\"]\n elastic_mapping_type: Optional[Field]\n has_only_quantity: Optional[\"ElasticsearchQuantity\"]\n nested_quantity: Optional[\"ElasticsearchQuantity\"]\n\n def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n elastic_mapping_type: Optional[Field] = None,\n has_only_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n nested_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n ):\n \"\"\"Initialise the quantity from its name, aliases and mapping type.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n super().__init__(name, backend_field, length_quantity)\n\n self.elastic_mapping_type = (\n Keyword if elastic_mapping_type is None else elastic_mapping_type\n )\n self.has_only_quantity = has_only_quantity\n self.nested_quantity = nested_quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticsearchQuantity.__init__","title":"__init__(name, backend_field=None, length_quantity=None, elastic_mapping_type=None, has_only_quantity=None, nested_quantity=None)
","text":"Initialise the quantity from its name, aliases and mapping type.
Parameters:
Name Type Description Defaultname
str
The name of the quantity as used in the filter expressions.
requiredbackend_field
Optional[str]
The name of the field for this quantity in Elasticsearch, will be name
by default.
None
elastic_mapping_type
Optional[Field]
A decendent of an elasticsearch_dsl.Field
that denotes which mapping type was used in the Elasticsearch index.
None
length_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.
None
has_only_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS
) and quantities with this attribute.
None
nested_quantity
Optional[ElasticsearchQuantity]
To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.
None
Source code in optimade/filtertransformers/elasticsearch.py
def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n elastic_mapping_type: Optional[Field] = None,\n has_only_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n nested_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n):\n \"\"\"Initialise the quantity from its name, aliases and mapping type.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n super().__init__(name, backend_field, length_quantity)\n\n self.elastic_mapping_type = (\n Keyword if elastic_mapping_type is None else elastic_mapping_type\n )\n self.has_only_quantity = has_only_quantity\n self.nested_quantity = nested_quantity\n
"},{"location":"api_reference/filtertransformers/mongo/","title":"mongo","text":"This submodule implements the MongoTransformer
, which takes the parsed filter and converts it to a valid pymongo/BSON query.
MongoTransformer
","text":" Bases: BaseTransformer
A filter transformer for the MongoDB backend.
Parses a lark tree into a dictionary representation to be used by pymongo or mongomock. Uses post-processing functions to handle some specific edge-cases for MongoDB.
Attributes:
Name Type Descriptionoperator_map
A map from comparison operators to the mongoDB specific versions.
inverse_operator_map
A map from operators to their logical inverse.
mapper
A resource mapper object that defines the expected fields and acts as a container for various field-related configuration.
Source code inoptimade/filtertransformers/mongo.py
class MongoTransformer(BaseTransformer):\n \"\"\"A filter transformer for the MongoDB backend.\n\n Parses a lark tree into a dictionary representation to be\n used by pymongo or mongomock. Uses post-processing functions\n to handle some specific edge-cases for MongoDB.\n\n Attributes:\n operator_map: A map from comparison operators\n to the mongoDB specific versions.\n inverse_operator_map: A map from operators to their\n logical inverse.\n mapper: A resource mapper object that defines the\n expected fields and acts as a container for\n various field-related configuration.\n\n \"\"\"\n\n operator_map = {\n \"<\": \"$lt\",\n \"<=\": \"$lte\",\n \">\": \"$gt\",\n \">=\": \"$gte\",\n \"!=\": \"$ne\",\n \"=\": \"$eq\",\n }\n\n inverse_operator_map = {\n \"$lt\": \"$gte\",\n \"$lte\": \"$gt\",\n \"$gt\": \"$lte\",\n \"$gte\": \"$lt\",\n \"$ne\": \"$eq\",\n \"$eq\": \"$ne\",\n \"$in\": \"$nin\",\n \"$nin\": \"$in\",\n }\n\n def postprocess(self, query: dict[str, Any]):\n \"\"\"Used to post-process the nested dictionary of the parsed query.\"\"\"\n query = self._apply_relationship_filtering(query)\n query = self._apply_length_operators(query)\n query = self._apply_unknown_or_null_filter(query)\n query = self._apply_has_only_filter(query)\n query = self._apply_mongo_id_filter(query)\n query = self._apply_mongo_date_filter(query)\n return query\n\n def value_list(self, arg):\n # value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\n # NOTE: no support for optional OPERATOR, yet, so this takes the\n # parsed values and returns an error if that is being attempted\n for value in arg:\n if str(value) in self.operator_map.keys():\n raise NotImplementedError(\n f\"OPERATOR {value} inside value_list {arg} not implemented.\"\n )\n\n return arg\n\n def value_zip(self, arg):\n # value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def value_zip_list(self, arg):\n # value_zip_list: value_zip ( \",\" value_zip )*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def expression(self, arg):\n # expression: expression_clause ( OR expression_clause )\n # expression with and without 'OR'\n return {\"$or\": arg} if len(arg) > 1 else arg[0]\n\n def expression_clause(self, arg):\n # expression_clause: expression_phrase ( AND expression_phrase )*\n # expression_clause with and without 'AND'\n return {\"$and\": arg} if len(arg) > 1 else arg[0]\n\n def expression_phrase(self, arg):\n # expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\n return self._recursive_expression_phrase(arg)\n\n @v_args(inline=True)\n def property_first_comparison(self, quantity, query):\n # property_first_comparison: property ( value_op_rhs | known_op_rhs | fuzzy_string_op_rhs | set_op_rhs |\n # set_zip_op_rhs | length_op_rhs )\n\n # Awkwardly, MongoDB will match null fields in $ne filters,\n # so we need to add a check for null equality in evey $ne query.\n if \"$ne\" in query:\n return {\"$and\": [{quantity: query}, {quantity: {\"$ne\": None}}]}\n\n # Check if a $size query is being made (indicating a length_op_rhs filter); if so, check for\n # a defined length alias to replace the $size call with the corresponding filter on the\n # length quantity then carefully merge the two queries.\n #\n # e.g. `(\"elements\", {\"$size\": 2, \"$all\": [\"Ag\", \"Au\"]})` should become\n # `{\"elements\": {\"$all\": [\"Ag\", \"Au\"]}, \"nelements\": 2}` if the `elements` -> `nelements`\n # length alias is defined.\n if \"$size\" in query:\n if (\n getattr(self.backend_mapping.get(quantity), \"length_quantity\", None)\n is not None\n ):\n size_query = {\n self.backend_mapping[ # type: ignore[union-attr]\n quantity\n ].length_quantity.backend_field: query.pop(\"$size\")\n }\n\n final_query = {}\n if query:\n final_query = {quantity: query}\n for q in size_query:\n if q in final_query:\n final_query[q].update(size_query[q])\n else:\n final_query[q] = size_query[q]\n\n return final_query\n\n return {quantity: query}\n\n def constant_first_comparison(self, arg):\n # constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\n return self.property_first_comparison(\n arg[2], {self.operator_map[self._reversed_operator_map[arg[1]]]: arg[0]}\n )\n\n @v_args(inline=True)\n def value_op_rhs(self, operator, value):\n # value_op_rhs: OPERATOR value\n return {self.operator_map[operator]: value}\n\n def known_op_rhs(self, arg):\n # known_op_rhs: IS ( KNOWN | UNKNOWN )\n # The OPTIMADE spec also required a type comparison with null, this must be post-processed\n # so here we use a special key \"#known\" which will get replaced in post-processing with the\n # expanded dict\n return {\"#known\": arg[1] == \"KNOWN\"}\n\n def fuzzy_string_op_rhs(self, arg):\n # fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\n\n # The WITH keyword may be omitted.\n if isinstance(arg[1], Token) and arg[1].type == \"WITH\":\n pattern = arg[2]\n else:\n pattern = arg[1]\n\n # CONTAINS\n if arg[0] == \"CONTAINS\":\n regex = f\"{pattern}\"\n elif arg[0] == \"STARTS\":\n regex = f\"^{pattern}\"\n elif arg[0] == \"ENDS\":\n regex = f\"{pattern}$\"\n return {\"$regex\": regex}\n\n def set_op_rhs(self, arg):\n # set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\n\n if len(arg) == 2:\n # only value without OPERATOR\n return {\"$in\": arg[1:]}\n\n if arg[1] == \"ALL\":\n return {\"$all\": arg[2]}\n\n if arg[1] == \"ANY\":\n return {\"$in\": arg[2]}\n\n if arg[1] == \"ONLY\":\n return {\"#only\": arg[2]}\n\n # value with OPERATOR\n raise NotImplementedError(\n f\"set_op_rhs not implemented for use with OPERATOR. Given: {arg}\"\n )\n\n def property(self, args):\n # property: IDENTIFIER ( \".\" IDENTIFIER )*\n quantity = super().property(args)\n if isinstance(quantity, Quantity):\n quantity = quantity.backend_field\n\n return \".\".join([quantity] + args[1:])\n\n def length_op_rhs(self, arg):\n # length_op_rhs: LENGTH [ OPERATOR ] value\n if len(arg) == 2 or (len(arg) == 3 and arg[1] == \"=\"):\n return {\"$size\": arg[-1]}\n\n if arg[1] in self.operator_map and arg[1] != \"!=\":\n # create an invalid query that needs to be post-processed\n # e.g. {'$size': {'$gt': 2}}, which is not allowed by Mongo.\n return {\"$size\": {self.operator_map[arg[1]]: arg[-1]}}\n\n raise NotImplementedError(\n f\"Operator {arg[1]} not implemented for LENGTH filter.\"\n )\n\n def set_zip_op_rhs(self, arg):\n # set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list |\n # ANY value_zip_list )\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def property_zip_addon(self, arg):\n # property_zip_addon: \":\" property (\":\" property)*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def _recursive_expression_phrase(self, arg: list) -> dict[str, Any]:\n \"\"\"Helper function for parsing `expression_phrase`. Recursively sorts out\n the correct precedence for `$not`, `$and` and `$or`.\n\n Parameters:\n arg: A list containing the expression to be evaluated and whether it\n is negated, e.g., `[\"NOT\", expr]` or just `[expr]`.\n\n Returns:\n The evaluated filter as a nested dictionary.\n\n \"\"\"\n\n def handle_not_and(arg: dict[str, list]) -> dict[str, list]:\n \"\"\"Handle the case of `~(A & B) -> (~A | ~B)`.\n\n We have to check for the special case in which the \"and\" was created\n by a previous NOT, e.g.,\n `NOT (NOT ({\"a\": {\"$eq\": 6}})) -> NOT({\"$and\": [{\"a\": {\"$ne\": 6}},{\"a\": {\"$ne\": None}}]})`\n\n Parameters:\n arg: A dictionary with key `\"$and\"` containing a list of expressions.\n\n Returns:\n A dictionary with key `\"$or\"` containing a list of the appropriate negated expressions.\n \"\"\"\n\n expr1 = arg[\"$and\"][0]\n expr2 = arg[\"$and\"][1]\n if expr1.keys() == expr2.keys():\n key = list(expr1.keys())[0]\n for e, f in itertools.permutations((expr1, expr2)):\n if e.get(key) == {\"$ne\": None}:\n return self._recursive_expression_phrase([\"NOT\", f])\n\n return {\n \"$or\": [\n self._recursive_expression_phrase([\"NOT\", subdict])\n for subdict in arg[\"$and\"]\n ]\n }\n\n def handle_not_or(arg: dict[str, list]) -> dict[str, list]:\n \"\"\"Handle the case of ~(A | B) -> (~A & ~B).\n\n !!! note\n Although the MongoDB `$nor` could be used here, it is not convenient as it\n will also return documents where the filtered field is missing when testing\n for inequality.\n\n Parameters:\n arg: A dictionary with key `\"$or\"` containing a list of expressions.\n\n Returns:\n A dictionary with key `\"$and\"` that lists the appropriate negated expressions.\n \"\"\"\n\n return {\n \"$and\": [\n self._recursive_expression_phrase([\"NOT\", subdict])\n for subdict in arg[\"$or\"]\n ]\n }\n\n if len(arg) == 1:\n # without NOT\n return arg[0]\n\n if \"$or\" in arg[1]:\n return handle_not_or(arg[1])\n\n if \"$and\" in arg[1]:\n return handle_not_and(arg[1])\n\n prop, expr = next(iter(arg[1].items()))\n operator, value = next(iter(expr.items()))\n if operator == \"$not\": # Case of double negation e.g. NOT(\"$not\":{ ...})\n return {prop: value}\n\n # If the NOT operator occurs at the lowest nesting level,\n # the expression can be simplified by using the opposite operator and removing the not.\n if operator in self.inverse_operator_map:\n filter_ = {prop: {self.inverse_operator_map[operator]: value}}\n if operator in (\"$in\", \"$eq\"):\n filter_ = {\"$and\": [filter_, {prop: {\"$ne\": None}}]} # type: ignore[dict-item]\n return filter_\n\n filter_ = {prop: {\"$not\": expr}}\n if \"#known\" in expr:\n return filter_\n return {\"$and\": [filter_, {prop: {\"$ne\": None}}]}\n\n def _apply_length_operators(self, filter_: dict) -> dict:\n \"\"\"Check for any invalid pymongo queries that involve applying a\n comparison operator to the length of a field, and transform\n them into a test for existence of the relevant entry, e.g.\n \"list LENGTH > 3\" becomes \"does the 4th list entry exist?\".\n\n \"\"\"\n\n def check_for_length_op_filter(_, expr):\n return (\n isinstance(expr, dict)\n and \"$size\" in expr\n and isinstance(expr[\"$size\"], dict)\n )\n\n def apply_length_op(subdict, prop, expr):\n # assumes that the dictionary only has one element by design\n # (we just made it above in the transformer)\n operator, value = list(expr[\"$size\"].items())[0]\n if operator in self.operator_map.values() and operator != \"$ne\":\n # worth being explicit here, I think\n _prop = None\n existence = None\n if operator == \"$gt\":\n _prop = f\"{prop}.{value + 1}\"\n existence = True\n elif operator == \"$gte\":\n _prop = f\"{prop}.{value}\"\n existence = True\n elif operator == \"$lt\":\n _prop = f\"{prop}.{value}\"\n existence = False\n elif operator == \"$lte\":\n _prop = f\"{prop}.{value + 1}\"\n existence = False\n if _prop is not None:\n subdict.pop(prop)\n subdict[_prop] = {\"$exists\": existence}\n\n return subdict\n\n return recursive_postprocessing(\n filter_,\n check_for_length_op_filter,\n apply_length_op,\n )\n\n def _apply_relationship_filtering(self, filter_: dict) -> dict:\n \"\"\"Check query for property names that match the entry\n types, and transform them as relationship filters rather than\n property filters.\n\n \"\"\"\n\n def check_for_entry_type(prop, _):\n return str(prop).count(\".\") == 1 and str(prop).split(\".\")[0] in (\n \"structures\",\n \"references\",\n )\n\n def replace_with_relationship(subdict, prop, expr):\n _prop, _field = str(prop).split(\".\")\n if _field != \"id\":\n raise NotImplementedError(\n f'Cannot filter relationships by field \"{_field}\", only \"id\" is supported.'\n )\n\n subdict[f\"relationships.{_prop}.data.{_field}\"] = expr\n subdict.pop(prop)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_entry_type, replace_with_relationship\n )\n\n def _apply_has_only_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces the magic key `\"#only\"`\n with the proper 'HAS ONLY' query.\n \"\"\"\n\n def check_for_only_filter(_, expr):\n \"\"\"Find cases where the magic key `\"#only\"` is in the query.\"\"\"\n return isinstance(expr, dict) and (\"#only\" in expr)\n\n def replace_only_filter(subdict: dict, prop: str, expr: dict):\n \"\"\"Replace the magic key `\"#only\"` (added by this transformer) with an `$elemMatch`-based query.\n\n The first part of the query selects all the documents that contain any value that does not\n match any target values for the property `prop`.\n Subsequently, this selection is inverted, to get the documents that only have\n the allowed values.\n This inversion also selects documents with edge-case values such as null or empty lists;\n these are removed in the second part of the query that makes sure that only documents\n with lists that have at least one value are selected.\n\n \"\"\"\n\n if \"$and\" not in subdict:\n subdict[\"$and\"] = []\n\n if prop.startswith(\"relationships.\"):\n if prop not in (\n \"relationships.references.data.id\",\n \"relationships.structures.data.id\",\n ):\n raise BadRequest(f\"Unable to query on unrecognised field {prop}.\")\n first_part_prop = \".\".join(prop.split(\".\")[:-1])\n subdict[\"$and\"].append(\n {\n first_part_prop: {\n \"$not\": {\"$elemMatch\": {\"id\": {\"$nin\": expr[\"#only\"]}}}\n }\n }\n )\n subdict[\"$and\"].append({first_part_prop + \".0\": {\"$exists\": True}})\n\n else:\n subdict[\"$and\"].append(\n {prop: {\"$not\": {\"$elemMatch\": {\"$nin\": expr[\"#only\"]}}}}\n )\n subdict[\"$and\"].append({prop + \".0\": {\"$exists\": True}})\n\n subdict.pop(prop)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_only_filter, replace_only_filter\n )\n\n def _apply_unknown_or_null_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces the check for\n KNOWN with a check for existence and a check for not null, and the\n inverse for UNKNOWN.\n\n \"\"\"\n\n def check_for_known_filter(_, expr):\n \"\"\"Find cases where the query dict looks like\n `{\"field\": {\"#known\": T/F}}` or\n `{\"field\": \"$not\": {\"#known\": T/F}}`, which is a magic word\n for KNOWN/UNKNOWN filters in this transformer.\n\n \"\"\"\n return isinstance(expr, dict) and (\n \"#known\" in expr or \"#known\" in expr.get(\"$not\", {})\n )\n\n def replace_known_filter_with_or(subdict, prop, expr):\n \"\"\"Replace magic key `\"#known\"` (added by this transformer) with the appropriate\n combination of `$exists` and/or test for nullity.\n combination of $exists and/or $eq/$ne null.\n\n \"\"\"\n not_ = set(expr.keys()) == {\"$not\"}\n if not_:\n expr = expr[\"$not\"]\n\n exists = expr[\"#known\"] ^ not_\n\n top_level_key = \"$or\"\n comparison_operator = \"$eq\"\n if exists:\n top_level_key = \"$and\"\n comparison_operator = \"$ne\"\n\n if top_level_key not in subdict:\n subdict[top_level_key] = []\n\n subdict[top_level_key].append({prop: {\"$exists\": exists}})\n subdict[top_level_key].append({prop: {comparison_operator: None}})\n\n subdict.pop(prop)\n\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_known_filter, replace_known_filter_with_or\n )\n\n def _apply_mongo_id_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces any operations\n on the special Mongodb `_id` key with the corresponding operation\n on a BSON `ObjectId` type.\n \"\"\"\n\n def check_for_id_key(prop, _):\n \"\"\"Find cases where the query dict is operating on the `_id` field.\"\"\"\n return prop == \"_id\"\n\n def replace_str_id_with_objectid(subdict, prop, expr):\n from bson import ObjectId\n\n for operator in subdict[prop]:\n val = subdict[prop][operator]\n if operator not in (\"$eq\", \"$ne\"):\n if self.mapper is not None:\n prop = self.mapper.get_optimade_field(prop)\n raise NotImplementedError(\n f\"Operator {operator} not supported for query on field {prop!r}, can only test for equality\"\n )\n if isinstance(val, str):\n subdict[prop][operator] = ObjectId(val)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_id_key, replace_str_id_with_objectid\n )\n\n def _apply_mongo_date_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces any operations\n on suspected timestamp properties with the corresponding operation\n on a BSON `DateTime` type.\n \"\"\"\n\n def check_for_timestamp_field(prop, _):\n \"\"\"Find cases where the query dict is operating on a timestamp field.\"\"\"\n if self.mapper is not None:\n prop = self.mapper.get_optimade_field(prop)\n return prop == \"last_modified\"\n\n def replace_str_date_with_datetime(subdict, prop, expr):\n \"\"\"Encode suspected dates in with BSON.\"\"\"\n import bson.json_util\n\n for operator in subdict[prop]:\n query_datetime = bson.json_util.loads(\n bson.json_util.dumps({\"$date\": subdict[prop][operator]}),\n json_options=bson.json_util.DEFAULT_JSON_OPTIONS.with_options(\n tz_aware=True, tzinfo=bson.tz_util.utc\n ),\n )\n if query_datetime.microsecond != 0:\n warnings.warn(\n f\"Query for timestamp {subdict[prop][operator]!r} for field {prop!r} contained microseconds, which is not RFC3339 compliant. \"\n \"This may cause undefined behaviour for the underlying database.\",\n TimestampNotRFCCompliant,\n )\n\n subdict[prop][operator] = query_datetime\n\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_timestamp_field, replace_str_date_with_datetime\n )\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
quantities: dict[str, Quantity]
property
writable
","text":"A mapping from the OPTIMADE field name to the corresponding Quantity
objects.
__default__(data, children, meta)
","text":"The default rule to call when no definition is found for a particular construct.
Source code inoptimade/filtertransformers/base_transformer.py
def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.__init__","title":"__init__(mapper=None)
","text":"Initialise the transformer object, optionally loading in a resource mapper for use when post-processing.
Source code inoptimade/filtertransformers/base_transformer.py
def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.filter","title":"filter(arg)
","text":"filter: expression*
Source code inoptimade/filtertransformers/base_transformer.py
def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.number","title":"number(number)
","text":"number: SIGNED_INT | SIGNED_FLOAT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.postprocess","title":"postprocess(query)
","text":"Used to post-process the nested dictionary of the parsed query.
Source code inoptimade/filtertransformers/mongo.py
def postprocess(self, query: dict[str, Any]):\n \"\"\"Used to post-process the nested dictionary of the parsed query.\"\"\"\n query = self._apply_relationship_filtering(query)\n query = self._apply_length_operators(query)\n query = self._apply_unknown_or_null_filter(query)\n query = self._apply_has_only_filter(query)\n query = self._apply_mongo_id_filter(query)\n query = self._apply_mongo_date_filter(query)\n return query\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.signed_int","title":"signed_int(number)
","text":"signed_int : SIGNED_INT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.string","title":"string(string)
","text":"string: ESCAPED_STRING
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.recursive_postprocessing","title":"recursive_postprocessing(filter_, condition, replacement)
","text":"Recursively descend into the query, checking each dictionary (contained in a list, or as an entry in another dictionary) for the condition passed. If the condition is true, apply the replacement to the dictionary.
Parameters:
Name Type Description Defaultfilter_
the filter_ to process.
requiredcondition
callable
a function that returns True if the replacement function should be applied. It should take as arguments the property and expression from the filter_, as would be returned by iterating over filter_.items()
.
replacement
callable
a function that returns the processed dictionary. It should take as arguments the dictionary to modify, the property and the expression (as described above).
required ExampleFor the simple case of replacing one field name with another, the following functions could be used:
def condition(prop, expr):\n return prop == \"field_name_old\"\n\ndef replacement(d, prop, expr):\n d[\"field_name_old\"] = d.pop(prop)\n\nfilter_ = recursive_postprocessing(\n filter_, condition, replacement\n)\n
Source code in optimade/filtertransformers/mongo.py
def recursive_postprocessing(filter_: Union[dict, list], condition, replacement):\n \"\"\"Recursively descend into the query, checking each dictionary\n (contained in a list, or as an entry in another dictionary) for\n the condition passed. If the condition is true, apply the\n replacement to the dictionary.\n\n Parameters:\n filter_ : the filter_ to process.\n condition (callable): a function that returns True if the\n replacement function should be applied. It should take\n as arguments the property and expression from the filter_,\n as would be returned by iterating over `filter_.items()`.\n replacement (callable): a function that returns the processed\n dictionary. It should take as arguments the dictionary\n to modify, the property and the expression (as described\n above).\n\n Example:\n For the simple case of replacing one field name with\n another, the following functions could be used:\n\n ```python\n def condition(prop, expr):\n return prop == \"field_name_old\"\n\n def replacement(d, prop, expr):\n d[\"field_name_old\"] = d.pop(prop)\n\n filter_ = recursive_postprocessing(\n filter_, condition, replacement\n )\n\n ```\n\n \"\"\"\n if isinstance(filter_, list):\n result = [recursive_postprocessing(q, condition, replacement) for q in filter_]\n return result\n\n if isinstance(filter_, dict):\n # this could potentially lead to memory leaks if the filter_ is *heavily* nested\n _cached_filter = copy.deepcopy(filter_)\n for prop, expr in filter_.items():\n if condition(prop, expr):\n _cached_filter = replacement(_cached_filter, prop, expr)\n elif isinstance(expr, list):\n _cached_filter[prop] = [\n recursive_postprocessing(q, condition, replacement) for q in expr\n ]\n return _cached_filter\n\n return filter_\n
"},{"location":"api_reference/models/baseinfo/","title":"baseinfo","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.VERSIONED_BASE_URL_PATTERN","title":"VERSIONED_BASE_URL_PATTERN = '^.+/v[0-1](\\\\.[0-9]+)*/?$'
module-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/","title":"entries","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship","title":"ReferenceRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class ReferenceRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"references\"]] = \"references\"\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship","title":"StructureRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class StructureRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"structures\"]] = \"structures\"\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship","title":"TypedRelationship
","text":" Bases: Relationship
optimade/models/entries.py
class TypedRelationship(Relationship):\n _req_type: ClassVar[str]\n\n @field_validator(\"data\", mode=\"after\")\n @classmethod\n def check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n ) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/index_metadb/","title":"index_metadb","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/","title":"jsonapi","text":"This module should reproduce JSON API v1.0 https://jsonapi.org/format/1.0/
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonLinkType","title":"JsonLinkType = Union[AnyUrl, Link]
module-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error","title":"Error
","text":" Bases: BaseModel
An error response
Source code inoptimade/models/jsonapi.py
class Error(BaseModel):\n \"\"\"An error response\"\"\"\n\n id: Annotated[\n Optional[str],\n StrictField(\n description=\"A unique identifier for this particular occurrence of the problem.\",\n ),\n ] = None\n links: Annotated[\n Optional[ErrorLinks], StrictField(description=\"A links object storing about\")\n ] = None\n status: Annotated[\n Optional[Annotated[str, BeforeValidator(str)]],\n StrictField(\n description=\"the HTTP status code applicable to this problem, expressed as a string value.\",\n ),\n ] = None\n code: Annotated[\n Optional[str],\n StrictField(\n description=\"an application-specific error code, expressed as a string value.\",\n ),\n ] = None\n title: Annotated[\n Optional[str],\n StrictField(\n description=\"A short, human-readable summary of the problem. \"\n \"It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.\",\n ),\n ] = None\n detail: Annotated[\n Optional[str],\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ] = None\n source: Annotated[\n Optional[ErrorSource],\n StrictField(\n description=\"An object containing references to the source of the error\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the error.\",\n ),\n ] = None\n\n def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.detail","title":"detail: Annotated[Optional[str], StrictField(description='A human-readable explanation specific to this occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship","title":"Relationship
","text":" Bases: BaseModel
Representation references from the resource object in which it's defined to other resource objects.
Source code inoptimade/models/jsonapi.py
class Relationship(BaseModel):\n \"\"\"Representation references from the resource object in which it's defined to other resource objects.\"\"\"\n\n links: Annotated[\n Optional[RelationshipLinks],\n StrictField(\n description=\"a links object containing at least one of the following: self, related\",\n ),\n ] = None\n data: Annotated[\n Optional[Union[BaseResource, list[BaseResource]]],\n StrictField(description=\"Resource linkage\"),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object that contains non-standard meta-information about the relationship.\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.data","title":"data: Annotated[Optional[Union[BaseResource, list[BaseResource]]], StrictField(description='Resource linkage')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.resource_json_schema_extra","title":"resource_json_schema_extra(schema, model)
","text":"Ensure id
and type
are the first two entries in the list required properties.
This requires that id
and type
are the first model fields defined for all sub-models of BaseResource
.
optimade/models/jsonapi.py
def resource_json_schema_extra(\n schema: dict[str, Any], model: type[\"BaseResource\"]\n) -> None:\n \"\"\"Ensure `id` and `type` are the first two entries in the list required properties.\n\n Note:\n This _requires_ that `id` and `type` are the _first_ model fields defined\n for all sub-models of `BaseResource`.\n\n \"\"\"\n if \"id\" not in schema.get(\"required\", []):\n schema[\"required\"] = [\"id\"] + schema.get(\"required\", [])\n if \"type\" not in schema.get(\"required\", []):\n required = []\n for field in schema.get(\"required\", []):\n required.append(field)\n if field == \"id\":\n # To make sure the property order match the listed properties,\n # this ensures \"type\" is added immediately after \"id\".\n required.append(\"type\")\n schema[\"required\"] = required\n
"},{"location":"api_reference/models/links/","title":"links","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate","title":"Aggregate
","text":" Bases: Enum
Enumeration of aggregate values
Source code inoptimade/models/links.py
class Aggregate(Enum):\n \"\"\"Enumeration of aggregate values\"\"\"\n\n OK = \"ok\"\n TEST = \"test\"\n STAGING = \"staging\"\n NO = \"no\"\n
"},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.NO","title":"NO = 'no'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.OK","title":"OK = 'ok'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.STAGING","title":"STAGING = 'staging'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.TEST","title":"TEST = 'test'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType","title":"LinkType
","text":" Bases: Enum
Enumeration of link_type values
Source code inoptimade/models/links.py
class LinkType(Enum):\n \"\"\"Enumeration of link_type values\"\"\"\n\n CHILD = \"child\"\n ROOT = \"root\"\n EXTERNAL = \"external\"\n PROVIDERS = \"providers\"\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinkType.CHILD","title":"CHILD = 'child'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.EXTERNAL","title":"EXTERNAL = 'external'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.PROVIDERS","title":"PROVIDERS = 'providers'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.ROOT","title":"ROOT = 'root'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/optimade_json/","title":"optimade_json","text":"Modified JSON API v1.0 for OPTIMADE API
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ValidIdentifier","title":"ValidIdentifier = Annotated[str, Field(pattern=IDENTIFIER_REGEX)]
module-attribute
","text":"A type that constrains strings to valid OPTIMADE identifiers (e.g., property names, ID strings).
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.warnings_json_schema_extra","title":"warnings_json_schema_extra(schema, model)
","text":"Update OpenAPI JSON schema model for Warning
.
type
is in the list required properties and in the correct place.status
property. This property is not allowed for Warning
, nor is it a part of the OPTIMADE definition of the Warning
object.Since type
is the last model field defined, it will simply be appended.
optimade/models/optimade_json.py
def warnings_json_schema_extra(schema: dict[str, Any], model: type[\"Warnings\"]) -> None:\n \"\"\"Update OpenAPI JSON schema model for `Warning`.\n\n * Ensure `type` is in the list required properties and in the correct place.\n * Remove `status` property.\n This property is not allowed for `Warning`, nor is it a part of the OPTIMADE\n definition of the `Warning` object.\n\n Note:\n Since `type` is the _last_ model field defined, it will simply be appended.\n\n \"\"\"\n if \"required\" in schema:\n if \"type\" not in schema[\"required\"]:\n schema[\"required\"].append(\"type\")\n else:\n schema[\"required\"] = [\"type\"]\n schema.get(\"properties\", {}).pop(\"status\", None)\n
"},{"location":"api_reference/models/references/","title":"references","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"api_reference/models/references/#optimade.models.references.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/responses/","title":"responses","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/structures/","title":"structures","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.CORRELATED_STRUCTURE_FIELDS","title":"CORRELATED_STRUCTURE_FIELDS = ({'dimension_types', 'nperiodic_dimensions'}, {'cartesian_site_positions', 'species_at_sites'}, {'nsites', 'cartesian_site_positions'}, {'species_at_sites', 'species'})
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.EPS","title":"EPS = 2 ** -23
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Vector3D_unknown","title":"Vector3D_unknown = Annotated[list[Optional[Annotated[float, BeforeValidator(float)]]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"api_reference/models/types/","title":"types","text":""},{"location":"api_reference/models/types/#optimade.models.types.AnnotatedType","title":"AnnotatedType = type(ChemicalSymbol)
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.ChemicalSymbol","title":"ChemicalSymbol = Annotated[str, Field(pattern=EXTENDED_CHEMICAL_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.ElementSymbol","title":"ElementSymbol = Annotated[str, Field(pattern=ELEMENT_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.NoneType","title":"NoneType = type(None)
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.OptionalType","title":"OptionalType = type(Optional[str])
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.SemanticVersion","title":"SemanticVersion = Annotated[str, Field(pattern=SEMVER_PATTERN, examples=['0.10.1', '1.0.0-rc.2', '1.2.3-rc.5+develop'])]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.UnionType","title":"UnionType = type(Union[str, int])
module-attribute
","text":""},{"location":"api_reference/models/utils/","title":"utils","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.ANONYMOUS_ELEMENTS","title":"ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150))
module-attribute
","text":"Returns the first 150 values of the anonymous element generator.
"},{"location":"api_reference/models/utils/#optimade.models.utils.ATOMIC_NUMBERS","title":"ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.CHEMICAL_FORMULA_REGEXP","title":"CHEMICAL_FORMULA_REGEXP = '(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\\\d+)?)+$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.ELEMENT_SYMBOLS_PATTERN","title":"ELEMENT_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.EXTENDED_CHEMICAL_SYMBOLS_PATTERN","title":"EXTENDED_CHEMICAL_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS + EXTRA_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.IDENTIFIER_REGEX","title":"IDENTIFIER_REGEX = '^[a-z_][a-z_0-9]+$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_KEYS","title":"OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_PREFIX","title":"OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SEMVER_PATTERN","title":"SEMVER_PATTERN = '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OptimadeField","title":"OptimadeField(default=PydanticUndefined, *, support=None, queryable=None, unit=None, **kwargs)
","text":"A wrapper around pydantic.Field
that adds OPTIMADE-specific field paramters queryable
, support
and unit
, indicating the corresponding support level in the specification and the physical unit of the field.
Parameters:
Name Type Description Defaultsupport
Optional[Union[str, SupportLevel]]
The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.
None
queryable
Optional[Union[str, SupportLevel]]
The support level corresponding to the queryablility of this field.
None
unit
Optional[str]
A string describing the unit of the field.
None
Returns:
Type DescriptionAny
The pydantic field with extra validation provided by StrictField
.
optimade/models/utils.py
def OptimadeField(\n default: \"Any\" = PydanticUndefined,\n *,\n support: Optional[Union[str, SupportLevel]] = None,\n queryable: Optional[Union[str, SupportLevel]] = None,\n unit: Optional[str] = None,\n **kwargs,\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that adds OPTIMADE-specific\n field paramters `queryable`, `support` and `unit`, indicating\n the corresponding support level in the specification and the\n physical unit of the field.\n\n Arguments:\n support: The support level of the field itself, i.e. whether the field\n can be null or omitted by an implementation.\n queryable: The support level corresponding to the queryablility\n of this field.\n unit: A string describing the unit of the field.\n\n Returns:\n The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].\n\n \"\"\"\n\n # Collect non-null keyword arguments to add to the Field schema\n if unit is not None:\n kwargs[\"unit\"] = unit\n\n if queryable is not None:\n if isinstance(queryable, str):\n queryable = SupportLevel(queryable.lower())\n kwargs[\"queryable\"] = queryable\n\n if support is not None:\n if isinstance(support, str):\n support = SupportLevel(support.lower())\n kwargs[\"support\"] = support\n\n return StrictField(default, **kwargs)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.StrictField","title":"StrictField(default=PydanticUndefined, *, description=None, **kwargs)
","text":"A wrapper around pydantic.Field
that does the following:
pydantic.Field
, except those used elsewhere to modify the schema in-place, e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g. \"unit\", \"queryable\" and \"sortable\".Parameters:
Name Type Description Defaultdefault
Any
The only non-keyword argument allowed for Field.
PydanticUndefined
description
Optional[str]
The description of the Field
; if this is not specified then a UserWarning
will be emitted.
None
**kwargs
Any
Extra keyword arguments to be passed to Field
.
{}
Raises:
Type DescriptionRuntimeError
If **kwargs
contains a key not found in the function signature of Field
, or in the extensions used by models in this package (see above).
Returns:
Type DescriptionAny
The pydantic Field
.
optimade/models/utils.py
def StrictField(\n default: \"Any\" = PydanticUndefined,\n *,\n description: Optional[str] = None,\n **kwargs: \"Any\",\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that does the following:\n\n - Forbids any \"extra\" keys that would be passed to `pydantic.Field`,\n except those used elsewhere to modify the schema in-place,\n e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g.\n \"unit\", \"queryable\" and \"sortable\".\n - Emits a warning when no description is provided.\n\n Arguments:\n default: The only non-keyword argument allowed for Field.\n description: The description of the `Field`; if this is not\n specified then a `UserWarning` will be emitted.\n **kwargs: Extra keyword arguments to be passed to `Field`.\n\n Raises:\n RuntimeError: If `**kwargs` contains a key not found in the\n function signature of `Field`, or in the extensions used\n by models in this package (see above).\n\n Returns:\n The pydantic `Field`.\n\n \"\"\"\n allowed_schema_and_field_keys = [\"pattern\"]\n\n allowed_keys = [\n \"pattern\",\n \"uniqueItems\",\n ] + OPTIMADE_SCHEMA_EXTENSION_KEYS\n _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]\n\n if _banned:\n raise RuntimeError(\n f\"Not creating StrictField({default!r}, **{kwargs!r}) with \"\n f\"forbidden keywords {_banned}.\"\n )\n\n # Handle description\n if description is None:\n warnings.warn(\n f\"No description provided for StrictField specified by {default!r}, \"\n f\"**{kwargs!r}.\"\n )\n else:\n kwargs[\"description\"] = description\n\n # OPTIMADE schema extensions\n json_schema_extra: dict[str, Any] = kwargs.pop(\"json_schema_extra\", {})\n\n # Go through all JSON Schema keys and add them to the json_schema_extra.\n for key in allowed_keys:\n if key not in kwargs:\n continue\n\n # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.\n schema_key = (\n f\"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}\"\n if key in OPTIMADE_SCHEMA_EXTENSION_KEYS\n else key\n )\n\n for key_variant in (key, schema_key):\n if key_variant in json_schema_extra:\n if json_schema_extra.pop(key_variant) != kwargs[key]:\n raise RuntimeError(\n f\"Conflicting values for {key} in json_schema_extra and kwargs.\"\n )\n\n json_schema_extra[schema_key] = (\n kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)\n )\n\n kwargs[\"json_schema_extra\"] = json_schema_extra\n\n return Field(default, **kwargs)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.anonymize_formula","title":"anonymize_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and returns the OPTIMADE chemical_formula_anonymous
representation, i.e., a reduced chemical formula comprising of element symbols drawn from A, B, C... ordered from largest proportion to smallest.
Returns:
Type Descriptionstr
The anonymous chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def anonymize_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n returns the OPTIMADE `chemical_formula_anonymous` representation, i.e., a reduced chemical formula comprising of element symbols\n drawn from A, B, C... ordered from largest proportion to smallest.\n\n Returns:\n The anonymous chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=False, anonymize=True)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.anonymous_element_generator","title":"anonymous_element_generator()
","text":"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.
Source code inoptimade/models/utils.py
def anonymous_element_generator() -> \"Generator[str, None, None]\":\n \"\"\"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.\"\"\"\n from string import ascii_lowercase\n\n for size in itertools.count(1):\n for tuple_strings in itertools.product(ascii_lowercase, repeat=size):\n list_strings = list(tuple_strings)\n list_strings[0] = list_strings[0].upper()\n yield \"\".join(list_strings)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.reduce_formula","title":"reduce_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.
Returns:
Type Descriptionstr
The reduced chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def reduce_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.\n\n Returns:\n The reduced chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=True, anonymize=False)\n
"},{"location":"api_reference/server/config/","title":"config","text":""},{"location":"api_reference/server/config/#optimade.server.config.CONFIG","title":"CONFIG: ServerConfig = ServerConfig()
module-attribute
","text":"This singleton loads the config from a hierarchy of sources (see customise_sources
) and makes it importable in the server code.
DEFAULT_CONFIG_FILE_PATH: str = str(Path.home().joinpath('.optimade.json'))
module-attribute
","text":"Default configuration file path.
This variable is used as the fallback value if the environment variable OPTIMADE_CONFIG_FILE
is not set.
Note
It is set to: pathlib.Path.home()/.optimade.json
For Unix-based systems (Linux) this will be equivalent to ~/.optimade.json
.
ConfigFileSettingsSource
","text":" Bases: PydanticBaseSettingsSource
Configuration file settings source.
Based on the example in the pydantic documentation, this class defines loading ServerConfig settings from a configuration file.
The file must be of either type JSON or YML/YAML.
Source code inoptimade/server/config.py
class ConfigFileSettingsSource(PydanticBaseSettingsSource):\n \"\"\"Configuration file settings source.\n\n Based on the example in the\n [pydantic documentation](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#customise-settings-sources),\n this class defines loading ServerConfig settings from a configuration file.\n\n The file must be of either type JSON or YML/YAML.\n \"\"\"\n\n def get_field_value(\n self, field: FieldInfo, field_name: str\n ) -> tuple[Any, str, bool]:\n \"\"\"Must be defined according to parent abstract class.\n\n It does not make sense to use it for this class, since 'extra' is set to\n 'allow'. We will instead just parse and take every key/field specified in the\n config file.\n \"\"\"\n raise NotImplementedError\n\n def parse_config_file(self) -> dict[str, Any]:\n \"\"\"Parse the config file and return a dictionary of its content.\"\"\"\n encoding = self.config.get(\"env_file_encoding\")\n config_file = Path(os.getenv(\"OPTIMADE_CONFIG_FILE\", DEFAULT_CONFIG_FILE_PATH))\n\n parsed_config_file = {}\n if config_file.is_file():\n config_file_content = config_file.read_text(encoding=encoding)\n\n try:\n parsed_config_file = json.loads(config_file_content)\n except json.JSONDecodeError as json_exc:\n try:\n # This can essentially also load JSON files, as JSON is a subset of\n # YAML v1, but I suspect it is not as rigorous\n parsed_config_file = yaml.safe_load(config_file_content)\n except yaml.YAMLError as yaml_exc:\n warnings.warn(\n f\"Unable to parse config file {config_file} as JSON or \"\n \"YAML, using the default settings instead..\\n\"\n f\"Errors:\\n JSON:\\n{json_exc}.\\n\\n YAML:\\n{yaml_exc}\"\n )\n else:\n warnings.warn(\n f\"Unable to find config file at {config_file}, using the default \"\n \"settings instead.\"\n )\n\n if parsed_config_file is None:\n # This can happen if the yaml loading doesn't succeed properly, e.g., if\n # the file is empty.\n warnings.warn(\n f\"Unable to load any settings from {config_file}, using the default \"\n \"settings instead.\"\n )\n parsed_config_file = {}\n\n if not isinstance(parsed_config_file, dict):\n warnings.warn(\n f\"Unable to parse config file {config_file} as a dictionary, using \"\n \"the default settings instead.\"\n )\n parsed_config_file = {}\n\n return parsed_config_file\n\n def __call__(self) -> dict[str, Any]:\n return self.parse_config_file()\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.__call__","title":"__call__()
","text":"Source code in optimade/server/config.py
def __call__(self) -> dict[str, Any]:\n return self.parse_config_file()\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.get_field_value","title":"get_field_value(field, field_name)
","text":"Must be defined according to parent abstract class.
It does not make sense to use it for this class, since 'extra' is set to 'allow'. We will instead just parse and take every key/field specified in the config file.
Source code inoptimade/server/config.py
def get_field_value(\n self, field: FieldInfo, field_name: str\n) -> tuple[Any, str, bool]:\n \"\"\"Must be defined according to parent abstract class.\n\n It does not make sense to use it for this class, since 'extra' is set to\n 'allow'. We will instead just parse and take every key/field specified in the\n config file.\n \"\"\"\n raise NotImplementedError\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.parse_config_file","title":"parse_config_file()
","text":"Parse the config file and return a dictionary of its content.
Source code inoptimade/server/config.py
def parse_config_file(self) -> dict[str, Any]:\n \"\"\"Parse the config file and return a dictionary of its content.\"\"\"\n encoding = self.config.get(\"env_file_encoding\")\n config_file = Path(os.getenv(\"OPTIMADE_CONFIG_FILE\", DEFAULT_CONFIG_FILE_PATH))\n\n parsed_config_file = {}\n if config_file.is_file():\n config_file_content = config_file.read_text(encoding=encoding)\n\n try:\n parsed_config_file = json.loads(config_file_content)\n except json.JSONDecodeError as json_exc:\n try:\n # This can essentially also load JSON files, as JSON is a subset of\n # YAML v1, but I suspect it is not as rigorous\n parsed_config_file = yaml.safe_load(config_file_content)\n except yaml.YAMLError as yaml_exc:\n warnings.warn(\n f\"Unable to parse config file {config_file} as JSON or \"\n \"YAML, using the default settings instead..\\n\"\n f\"Errors:\\n JSON:\\n{json_exc}.\\n\\n YAML:\\n{yaml_exc}\"\n )\n else:\n warnings.warn(\n f\"Unable to find config file at {config_file}, using the default \"\n \"settings instead.\"\n )\n\n if parsed_config_file is None:\n # This can happen if the yaml loading doesn't succeed properly, e.g., if\n # the file is empty.\n warnings.warn(\n f\"Unable to load any settings from {config_file}, using the default \"\n \"settings instead.\"\n )\n parsed_config_file = {}\n\n if not isinstance(parsed_config_file, dict):\n warnings.warn(\n f\"Unable to parse config file {config_file} as a dictionary, using \"\n \"the default settings instead.\"\n )\n parsed_config_file = {}\n\n return parsed_config_file\n
"},{"location":"api_reference/server/config/#optimade.server.config.LogLevel","title":"LogLevel
","text":" Bases: Enum
Replication of logging LogLevels
notset
debug
info
warning
error
critical
optimade/server/config.py
class LogLevel(Enum):\n \"\"\"Replication of logging LogLevels\n\n - `notset`\n - `debug`\n - `info`\n - `warning`\n - `error`\n - `critical`\n\n \"\"\"\n\n NOTSET = \"notset\"\n DEBUG = \"debug\"\n INFO = \"info\"\n WARNING = \"warning\"\n ERROR = \"error\"\n CRITICAL = \"critical\"\n
"},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.CRITICAL","title":"CRITICAL = 'critical'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.DEBUG","title":"DEBUG = 'debug'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.ERROR","title":"ERROR = 'error'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.INFO","title":"INFO = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.NOTSET","title":"NOTSET = 'notset'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.WARNING","title":"WARNING = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig","title":"ServerConfig
","text":" Bases: BaseSettings
This class stores server config parameters in a way that can be easily extended for new config file types.
Source code inoptimade/server/config.py
class ServerConfig(BaseSettings):\n \"\"\"This class stores server config parameters in a way that\n can be easily extended for new config file types.\n \"\"\"\n\n model_config = SettingsConfigDict(\n env_prefix=\"optimade_\",\n extra=\"allow\",\n env_file_encoding=\"utf-8\",\n case_sensitive=False,\n )\n\n debug: Annotated[\n bool,\n Field(\n description=\"Turns on Debug Mode for the OPTIMADE Server implementation\",\n ),\n ] = False\n\n insert_test_data: Annotated[\n bool,\n Field(\n description=(\n \"Insert test data into each collection on server initialisation. If true, \"\n \"the configured backend will be populated with test data on server start. \"\n \"Should be disabled for production usage.\"\n ),\n ),\n ] = True\n\n insert_from_jsonl: Annotated[\n Optional[Path],\n Field(\n description=(\n \"The absolute path to an OPTIMADE JSONL file to use to initialize the database. \"\n \"A unique index will be created over the ID to avoid duplication over multiple runs.\"\n )\n ),\n ] = None\n\n use_real_mongo: Annotated[\n Optional[bool],\n Field(description=\"DEPRECATED: force usage of MongoDB over any other backend.\"),\n ] = None\n\n database_backend: Annotated[\n SupportedBackend,\n Field(\n description=\"Which database backend to use out of the supported backends.\",\n ),\n ] = SupportedBackend.MONGOMOCK\n\n elastic_hosts: Annotated[\n Optional[Union[str, list[str], dict[str, Any], list[dict[str, Any]]]],\n Field(\n description=\"Host settings to pass through to the `Elasticsearch` class.\"\n ),\n ] = None\n\n mongo_count_timeout: Annotated[\n int,\n Field(\n description=(\n \"Number of seconds to allow MongoDB to perform a full database count \"\n \"before falling back to `null`. This operation can require a full COLLSCAN\"\n \" for empty queries which can be prohibitively slow if the database does \"\n \"not fit into the active set, hence a timeout can drastically speed-up \"\n \"response times.\"\n ),\n ),\n ] = 5\n\n mongo_database: Annotated[\n str, Field(description=\"Mongo database for collection data\")\n ] = \"optimade\"\n mongo_uri: Annotated[str, Field(description=\"URI for the Mongo server\")] = (\n \"localhost:27017\"\n )\n links_collection: Annotated[\n str, Field(description=\"Mongo collection name for /links endpoint resources\")\n ] = \"links\"\n references_collection: Annotated[\n str,\n Field(\n description=\"Mongo collection name for /references endpoint resources\",\n ),\n ] = \"references\"\n structures_collection: Annotated[\n str,\n Field(\n description=\"Mongo collection name for /structures endpoint resources\",\n ),\n ] = \"structures\"\n page_limit: Annotated[\n int, Field(description=\"Default number of resources per page\")\n ] = 20\n page_limit_max: Annotated[\n int, Field(description=\"Max allowed number of resources per page\")\n ] = 500\n default_db: Annotated[\n str,\n Field(\n description=(\n \"ID of /links endpoint resource for the chosen default OPTIMADE \"\n \"implementation (only relevant for the index meta-database)\"\n ),\n ),\n ] = \"test_server\"\n root_path: Annotated[\n Optional[str],\n Field(\n description=(\n \"Sets the FastAPI app `root_path` parameter. This can be used to serve the\"\n \" API under a path prefix behind a proxy or as a sub-application of \"\n \"another FastAPI app. See \"\n \"https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path\"\n \" for details.\"\n ),\n ),\n ] = None\n base_url: Annotated[\n Optional[str], Field(description=\"Base URL for this implementation\")\n ] = None\n implementation: Annotated[\n Implementation,\n Field(\n description=\"Introspective information about this OPTIMADE implementation\",\n ),\n ] = Implementation(\n name=\"OPTIMADE Python Tools\",\n version=__version__,\n source_url=\"https://github.com/Materials-Consortia/optimade-python-tools\",\n maintainer={\"email\": \"dev@optimade.org\"},\n issue_tracker=(\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues\"\n ),\n homepage=\"https://optimade.org/optimade-python-tools\",\n )\n index_base_url: Annotated[\n Optional[AnyHttpUrl],\n Field(\n description=(\n \"An optional link to the base URL for the index meta-database of the \"\n \"provider.\"\n ),\n ),\n ] = None\n provider: Annotated[\n Provider,\n Field(\n description=(\n \"General information about the provider of this OPTIMADE implementation\"\n ),\n ),\n ] = Provider(\n prefix=\"exmpl\",\n name=\"Example provider\",\n description=(\n \"Provider used for examples, not to be assigned to a real database\"\n ),\n homepage=\"https://example.com\",\n )\n provider_fields: Annotated[\n dict[\n Literal[\"links\", \"references\", \"structures\"],\n list[Union[str, dict[Literal[\"name\", \"type\", \"unit\", \"description\"], str]]],\n ],\n Field(\n description=(\n \"A list of additional fields to be served with the provider's prefix \"\n \"attached, broken down by endpoint.\"\n ),\n ),\n ] = {}\n aliases: Annotated[\n dict[Literal[\"links\", \"references\", \"structures\"], dict[str, str]],\n Field(\n description=(\n \"A mapping between field names in the database with their corresponding \"\n \"OPTIMADE field names, broken down by endpoint.\"\n ),\n ),\n ] = {}\n length_aliases: Annotated[\n dict[Literal[\"links\", \"references\", \"structures\"], dict[str, str]],\n Field(\n description=(\n \"A mapping between a list property (or otherwise) and an integer property \"\n \"that defines the length of that list, for example elements -> nelements. \"\n \"The standard aliases are applied first, so this dictionary must refer to \"\n \"the API fields, not the database fields.\"\n ),\n ),\n ] = {}\n index_links_path: Annotated[\n Path,\n Field(\n description=(\n \"Absolute path to a JSON file containing the MongoDB collecton of links \"\n \"entries (documents) to serve under the /links endpoint of the index \"\n \"meta-database. NB! As suggested in the previous sentence, these will \"\n \"only be served when using a MongoDB-based backend.\"\n ),\n ),\n ] = Path(__file__).parent.joinpath(\"index_links.json\")\n\n is_index: Annotated[\n Optional[bool],\n Field(\n description=(\n \"A runtime setting to dynamically switch between index meta-database and \"\n \"normal OPTIMADE servers. Used for switching behaviour of e.g., \"\n \"`meta->optimade_schema` values in the response. Any provided value may \"\n \"be overridden when used with the reference server implementation.\"\n ),\n ),\n ] = False\n\n schema_url: Annotated[\n Optional[Union[str, AnyHttpUrl]],\n Field(\n description=(\n \"A URL that will be provided in the `meta->schema` field for every response\"\n ),\n ),\n ] = f\"https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json\"\n\n custom_landing_page: Annotated[\n Optional[Union[str, Path]],\n Field(\n description=(\n \"The location of a custom landing page (Jinja template) to use for the API.\"\n ),\n ),\n ] = None\n\n index_schema_url: Annotated[\n Optional[Union[str, AnyHttpUrl]],\n Field(\n description=(\n \"A URL that will be provided in the `meta->schema` field for every \"\n \"response from the index meta-database.\"\n ),\n ),\n ] = f\"https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json\"\n\n log_level: Annotated[\n LogLevel, Field(description=\"Logging level for the OPTIMADE server.\")\n ] = LogLevel.INFO\n log_dir: Annotated[\n Path,\n Field(\n description=\"Folder in which log files will be saved.\",\n ),\n ] = Path(\"/var/log/optimade/\")\n validate_query_parameters: Annotated[\n Optional[bool],\n Field(\n description=(\n \"If True, the server will check whether the query parameters given in the \"\n \"request are correct.\"\n ),\n ),\n ] = True\n\n validate_api_response: Annotated[\n Optional[bool],\n Field(\n description=(\n \"If False, data from the database will not undergo validation before being\"\n \" emitted by the API, and only the mapping of aliases will occur.\"\n ),\n ),\n ] = True\n\n @field_validator(\"insert_from_jsonl\", mode=\"before\")\n @classmethod\n def check_jsonl_path(cls, value: Any) -> Optional[Path]:\n \"\"\"Check that the path to the JSONL file is valid.\"\"\"\n if value in (\"null\", \"\"):\n return None\n\n return value\n\n @field_validator(\"implementation\", mode=\"before\")\n @classmethod\n def set_implementation_version(cls, value: Any) -> dict[str, Any]:\n \"\"\"Set defaults and modify bypassed value(s)\"\"\"\n if not isinstance(value, dict):\n if isinstance(value, Implementation):\n value = value.model_dump()\n else:\n raise TypeError(\n f\"Expected a dict or Implementation instance, got {type(value)}\"\n )\n\n res = {\"version\": __version__}\n res.update(value)\n return res\n\n @model_validator(mode=\"after\")\n def use_real_mongo_override(self) -> \"ServerConfig\":\n \"\"\"Overrides the `database_backend` setting with MongoDB and\n raises a deprecation warning.\n \"\"\"\n use_real_mongo = self.use_real_mongo\n\n # Remove from model\n del self.use_real_mongo\n\n # Remove from set of user-defined fields\n if \"use_real_mongo\" in self.model_fields_set:\n self.model_fields_set.remove(\"use_real_mongo\")\n\n if use_real_mongo is not None:\n warnings.warn(\n \"'use_real_mongo' is deprecated, please set the appropriate 'database_backend' \"\n \"instead.\",\n DeprecationWarning,\n )\n\n if use_real_mongo:\n self.database_backend = SupportedBackend.MONGODB\n\n return self\n\n @classmethod\n def settings_customise_sources(\n cls,\n settings_cls: type[BaseSettings],\n init_settings: PydanticBaseSettingsSource,\n env_settings: PydanticBaseSettingsSource,\n dotenv_settings: PydanticBaseSettingsSource,\n file_secret_settings: PydanticBaseSettingsSource,\n ) -> tuple[PydanticBaseSettingsSource, ...]:\n \"\"\"\n **Priority of config settings sources**:\n\n 1. Passed arguments upon initialization of\n [`ServerConfig`][optimade.server.config.ServerConfig].\n 2. Environment variables, matching the syntax: `\"OPTIMADE_\"` or `\"optimade_\"` +\n `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or\n `optimade_log_dir=~/logs_dir/optimade/`.\n 3. Configuration file (JSON/YAML) taken from:\n 1. Environment variable `OPTIMADE_CONFIG_FILE`.\n 2. Default location (see\n [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).\n 4. Settings from secret file (see\n [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)\n for more information).\n\n \"\"\"\n return (\n init_settings,\n env_settings,\n ConfigFileSettingsSource(settings_cls),\n file_secret_settings,\n )\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.aliases","title":"aliases: Annotated[dict[Literal['links', 'references', 'structures'], dict[str, str]], Field(description='A mapping between field names in the database with their corresponding OPTIMADE field names, broken down by endpoint.')] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.base_url","title":"base_url: Annotated[Optional[str], Field(description='Base URL for this implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.custom_landing_page","title":"custom_landing_page: Annotated[Optional[Union[str, Path]], Field(description='The location of a custom landing page (Jinja template) to use for the API.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.database_backend","title":"database_backend: Annotated[SupportedBackend, Field(description='Which database backend to use out of the supported backends.')] = SupportedBackend.MONGOMOCK
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.debug","title":"debug: Annotated[bool, Field(description='Turns on Debug Mode for the OPTIMADE Server implementation')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.default_db","title":"default_db: Annotated[str, Field(description='ID of /links endpoint resource for the chosen default OPTIMADE implementation (only relevant for the index meta-database)')] = 'test_server'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.elastic_hosts","title":"elastic_hosts: Annotated[Optional[Union[str, list[str], dict[str, Any], list[dict[str, Any]]]], Field(description='Host settings to pass through to the `Elasticsearch` class.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.implementation","title":"implementation: Annotated[Implementation, Field(description='Introspective information about this OPTIMADE implementation')] = Implementation(name='OPTIMADE Python Tools', version=__version__, source_url='https://github.com/Materials-Consortia/optimade-python-tools', maintainer={'email': 'dev@optimade.org'}, issue_tracker='https://github.com/Materials-Consortia/optimade-python-tools/issues', homepage='https://optimade.org/optimade-python-tools')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_base_url","title":"index_base_url: Annotated[Optional[AnyHttpUrl], Field(description='An optional link to the base URL for the index meta-database of the provider.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_links_path","title":"index_links_path: Annotated[Path, Field(description='Absolute path to a JSON file containing the MongoDB collecton of links entries (documents) to serve under the /links endpoint of the index meta-database. NB! As suggested in the previous sentence, these will only be served when using a MongoDB-based backend.')] = Path(__file__).parent.joinpath('index_links.json')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_schema_url","title":"index_schema_url: Annotated[Optional[Union[str, AnyHttpUrl]], Field(description='A URL that will be provided in the `meta->schema` field for every response from the index meta-database.')] = f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.insert_from_jsonl","title":"insert_from_jsonl: Annotated[Optional[Path], Field(description='The absolute path to an OPTIMADE JSONL file to use to initialize the database. A unique index will be created over the ID to avoid duplication over multiple runs.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.insert_test_data","title":"insert_test_data: Annotated[bool, Field(description='Insert test data into each collection on server initialisation. If true, the configured backend will be populated with test data on server start. Should be disabled for production usage.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.is_index","title":"is_index: Annotated[Optional[bool], Field(description='A runtime setting to dynamically switch between index meta-database and normal OPTIMADE servers. Used for switching behaviour of e.g., `meta->optimade_schema` values in the response. Any provided value may be overridden when used with the reference server implementation.')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.length_aliases","title":"length_aliases: Annotated[dict[Literal['links', 'references', 'structures'], dict[str, str]], Field(description='A mapping between a list property (or otherwise) and an integer property that defines the length of that list, for example elements -> nelements. The standard aliases are applied first, so this dictionary must refer to the API fields, not the database fields.')] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.links_collection","title":"links_collection: Annotated[str, Field(description='Mongo collection name for /links endpoint resources')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.log_dir","title":"log_dir: Annotated[Path, Field(description='Folder in which log files will be saved.')] = Path('/var/log/optimade/')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.log_level","title":"log_level: Annotated[LogLevel, Field(description='Logging level for the OPTIMADE server.')] = LogLevel.INFO
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.model_config","title":"model_config = SettingsConfigDict(env_prefix='optimade_', extra='allow', env_file_encoding='utf-8', case_sensitive=False)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_count_timeout","title":"mongo_count_timeout: Annotated[int, Field(description='Number of seconds to allow MongoDB to perform a full database count before falling back to `null`. This operation can require a full COLLSCAN for empty queries which can be prohibitively slow if the database does not fit into the active set, hence a timeout can drastically speed-up response times.')] = 5
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_database","title":"mongo_database: Annotated[str, Field(description='Mongo database for collection data')] = 'optimade'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_uri","title":"mongo_uri: Annotated[str, Field(description='URI for the Mongo server')] = 'localhost:27017'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.page_limit","title":"page_limit: Annotated[int, Field(description='Default number of resources per page')] = 20
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.page_limit_max","title":"page_limit_max: Annotated[int, Field(description='Max allowed number of resources per page')] = 500
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.provider","title":"provider: Annotated[Provider, Field(description='General information about the provider of this OPTIMADE implementation')] = Provider(prefix='exmpl', name='Example provider', description='Provider used for examples, not to be assigned to a real database', homepage='https://example.com')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.provider_fields","title":"provider_fields: Annotated[dict[Literal['links', 'references', 'structures'], list[Union[str, dict[Literal['name', 'type', 'unit', 'description'], str]]]], Field(description=\"A list of additional fields to be served with the provider's prefix attached, broken down by endpoint.\")] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.references_collection","title":"references_collection: Annotated[str, Field(description='Mongo collection name for /references endpoint resources')] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.root_path","title":"root_path: Annotated[Optional[str], Field(description='Sets the FastAPI app `root_path` parameter. This can be used to serve the API under a path prefix behind a proxy or as a sub-application of another FastAPI app. See https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path for details.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.schema_url","title":"schema_url: Annotated[Optional[Union[str, AnyHttpUrl]], Field(description='A URL that will be provided in the `meta->schema` field for every response')] = f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.structures_collection","title":"structures_collection: Annotated[str, Field(description='Mongo collection name for /structures endpoint resources')] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.use_real_mongo","title":"use_real_mongo: Annotated[Optional[bool], Field(description='DEPRECATED: force usage of MongoDB over any other backend.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.validate_api_response","title":"validate_api_response: Annotated[Optional[bool], Field(description='If False, data from the database will not undergo validation before being emitted by the API, and only the mapping of aliases will occur.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.validate_query_parameters","title":"validate_query_parameters: Annotated[Optional[bool], Field(description='If True, the server will check whether the query parameters given in the request are correct.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.check_jsonl_path","title":"check_jsonl_path(value)
classmethod
","text":"Check that the path to the JSONL file is valid.
Source code inoptimade/server/config.py
@field_validator(\"insert_from_jsonl\", mode=\"before\")\n@classmethod\ndef check_jsonl_path(cls, value: Any) -> Optional[Path]:\n \"\"\"Check that the path to the JSONL file is valid.\"\"\"\n if value in (\"null\", \"\"):\n return None\n\n return value\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.set_implementation_version","title":"set_implementation_version(value)
classmethod
","text":"Set defaults and modify bypassed value(s)
Source code inoptimade/server/config.py
@field_validator(\"implementation\", mode=\"before\")\n@classmethod\ndef set_implementation_version(cls, value: Any) -> dict[str, Any]:\n \"\"\"Set defaults and modify bypassed value(s)\"\"\"\n if not isinstance(value, dict):\n if isinstance(value, Implementation):\n value = value.model_dump()\n else:\n raise TypeError(\n f\"Expected a dict or Implementation instance, got {type(value)}\"\n )\n\n res = {\"version\": __version__}\n res.update(value)\n return res\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.settings_customise_sources","title":"settings_customise_sources(settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings)
classmethod
","text":"Priority of config settings sources:
ServerConfig
.\"OPTIMADE_\"
or \"optimade_\"
+ <config_name>
, e.g., OPTIMADE_LOG_LEVEL=debug
or optimade_log_dir=~/logs_dir/optimade/
.OPTIMADE_CONFIG_FILE
.optimade/server/config.py
@classmethod\ndef settings_customise_sources(\n cls,\n settings_cls: type[BaseSettings],\n init_settings: PydanticBaseSettingsSource,\n env_settings: PydanticBaseSettingsSource,\n dotenv_settings: PydanticBaseSettingsSource,\n file_secret_settings: PydanticBaseSettingsSource,\n) -> tuple[PydanticBaseSettingsSource, ...]:\n \"\"\"\n **Priority of config settings sources**:\n\n 1. Passed arguments upon initialization of\n [`ServerConfig`][optimade.server.config.ServerConfig].\n 2. Environment variables, matching the syntax: `\"OPTIMADE_\"` or `\"optimade_\"` +\n `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or\n `optimade_log_dir=~/logs_dir/optimade/`.\n 3. Configuration file (JSON/YAML) taken from:\n 1. Environment variable `OPTIMADE_CONFIG_FILE`.\n 2. Default location (see\n [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).\n 4. Settings from secret file (see\n [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)\n for more information).\n\n \"\"\"\n return (\n init_settings,\n env_settings,\n ConfigFileSettingsSource(settings_cls),\n file_secret_settings,\n )\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.use_real_mongo_override","title":"use_real_mongo_override()
","text":"Overrides the database_backend
setting with MongoDB and raises a deprecation warning.
optimade/server/config.py
@model_validator(mode=\"after\")\ndef use_real_mongo_override(self) -> \"ServerConfig\":\n \"\"\"Overrides the `database_backend` setting with MongoDB and\n raises a deprecation warning.\n \"\"\"\n use_real_mongo = self.use_real_mongo\n\n # Remove from model\n del self.use_real_mongo\n\n # Remove from set of user-defined fields\n if \"use_real_mongo\" in self.model_fields_set:\n self.model_fields_set.remove(\"use_real_mongo\")\n\n if use_real_mongo is not None:\n warnings.warn(\n \"'use_real_mongo' is deprecated, please set the appropriate 'database_backend' \"\n \"instead.\",\n DeprecationWarning,\n )\n\n if use_real_mongo:\n self.database_backend = SupportedBackend.MONGODB\n\n return self\n
"},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend","title":"SupportedBackend
","text":" Bases: Enum
Enumeration of supported database backends
elastic
: Elasticsearch.mongodb
: MongoDB.mongomock
: Also MongoDB, but instead of using the pymongo
driver to connect to a live Mongo database instance, this will use the mongomock
driver, creating an in-memory database, which is mainly used for testing.optimade/server/config.py
class SupportedBackend(Enum):\n \"\"\"Enumeration of supported database backends\n\n - `elastic`: [Elasticsearch](https://www.elastic.co/).\n - `mongodb`: [MongoDB](https://www.mongodb.com/).\n - `mongomock`: Also MongoDB, but instead of using the\n [`pymongo`](https://pymongo.readthedocs.io/) driver to connect to a live Mongo\n database instance, this will use the\n [`mongomock`](https://github.com/mongomock/mongomock) driver, creating an\n in-memory database, which is mainly used for testing.\n\n \"\"\"\n\n ELASTIC = \"elastic\"\n MONGODB = \"mongodb\"\n MONGOMOCK = \"mongomock\"\n
"},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.ELASTIC","title":"ELASTIC = 'elastic'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.MONGODB","title":"MONGODB = 'mongodb'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.MONGOMOCK","title":"MONGOMOCK = 'mongomock'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/exception_handlers/","title":"exception_handlers","text":""},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.OPTIMADE_EXCEPTIONS","title":"OPTIMADE_EXCEPTIONS: Iterable[tuple[type[Exception], Callable[[Request, Exception], JSONAPIResponse]]] = [(StarletteHTTPException, http_exception_handler), (OptimadeHTTPException, http_exception_handler), (RequestValidationError, request_validation_exception_handler), (ValidationError, validation_exception_handler), (VisitError, grammar_not_implemented_handler), (NotImplementedError, not_implemented_handler), (Exception, general_exception_handler)]
module-attribute
","text":"A tuple of all pairs of exceptions and handler functions that allow for appropriate responses to be returned in certain scenarios according to the OPTIMADE specification.
To use these in FastAPI app code:
from fastapi import FastAPI\napp = FastAPI()\nfor exception, handler in OPTIMADE_EXCEPTIONS:\n app.add_exception_handler(exception, handler)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.general_exception","title":"general_exception(request, exc, status_code=500, errors=None)
","text":"Handle an exception
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Exception
The exception being raised.
requiredstatus_code
int
The returned HTTP status code for the error response.
500
errors
Optional[list[OptimadeError]]
List of error resources as defined in the OPTIMADE specification.
None
Returns:
Type DescriptionJSONAPIResponse
A JSON HTTP response based on ErrorResponse
.
optimade/server/exception_handlers.py
def general_exception(\n request: Request,\n exc: Exception,\n status_code: int = 500, # A status_code in `exc` will take precedence\n errors: Optional[list[OptimadeError]] = None,\n) -> JSONAPIResponse:\n \"\"\"Handle an exception\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n status_code: The returned HTTP status code for the error response.\n errors: List of error resources as defined in\n [the OPTIMADE specification](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#json-response-schema-common-fields).\n\n Returns:\n A JSON HTTP response based on [`ErrorResponse`][optimade.models.responses.ErrorResponse].\n\n \"\"\"\n debug_info = {}\n if CONFIG.debug:\n tb = \"\".join(\n traceback.format_exception(type(exc), value=exc, tb=exc.__traceback__)\n )\n LOGGER.error(\"Traceback:\\n%s\", tb)\n debug_info[f\"_{CONFIG.provider.prefix}_traceback\"] = tb\n\n try:\n http_response_code = int(exc.status_code) # type: ignore[attr-defined]\n except AttributeError:\n http_response_code = int(status_code)\n\n try:\n title = str(exc.title) # type: ignore[attr-defined]\n except AttributeError:\n title = str(exc.__class__.__name__)\n\n try:\n detail = str(exc.detail) # type: ignore[attr-defined]\n except AttributeError:\n detail = str(exc)\n\n if errors is None:\n errors = [OptimadeError(detail=detail, status=http_response_code, title=title)]\n\n response = ErrorResponse(\n meta=meta_values(\n url=request.url,\n data_returned=0,\n data_available=0,\n more_data_available=False,\n schema=CONFIG.schema_url,\n **debug_info,\n ),\n errors=errors,\n )\n\n return JSONAPIResponse(\n status_code=http_response_code,\n content=jsonable_encoder(response, exclude_unset=True),\n )\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.general_exception_handler","title":"general_exception_handler(request, exc)
","text":"Catch all Python Exceptions not handled by other exception handlers
Pass-through directly to general_exception()
.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Exception
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def general_exception_handler(request: Request, exc: Exception) -> JSONAPIResponse:\n \"\"\"Catch all Python Exceptions not handled by other exception handlers\n\n Pass-through directly to [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.grammar_not_implemented_handler","title":"grammar_not_implemented_handler(request, exc)
","text":"Handle an error raised by Lark during filter transformation
All errors raised during filter transformation are wrapped in the Lark VisitError
. According to the OPTIMADE specification, these errors are repurposed to be 501 NotImplementedErrors.
For special exceptions, like BadRequest
, we pass-through to general_exception()
, since they should not return a 501 NotImplementedError.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
VisitError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def grammar_not_implemented_handler(\n request: Request, exc: VisitError\n) -> JSONAPIResponse:\n \"\"\"Handle an error raised by Lark during filter transformation\n\n All errors raised during filter transformation are wrapped in the Lark `VisitError`.\n According to the OPTIMADE specification, these errors are repurposed to be 501 NotImplementedErrors.\n\n For special exceptions, like [`BadRequest`][optimade.exceptions.BadRequest], we pass-through to\n [`general_exception()`][optimade.server.exception_handlers.general_exception], since they should not\n return a 501 NotImplementedError.\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n pass_through_exceptions = (BadRequest,)\n\n orig_exc = getattr(exc, \"orig_exc\", None)\n if isinstance(orig_exc, pass_through_exceptions):\n return general_exception(request, orig_exc)\n\n rule = getattr(exc.obj, \"data\", getattr(exc.obj, \"type\", str(exc)))\n\n status = 501\n title = \"NotImplementedError\"\n detail = (\n f\"Error trying to process rule '{rule}'\"\n if not str(exc.orig_exc)\n else str(exc.orig_exc)\n )\n error = OptimadeError(detail=detail, status=status, title=title)\n return general_exception(request, exc, status_code=status, errors=[error])\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.http_exception_handler","title":"http_exception_handler(request, exc)
","text":"Handle a general HTTP Exception from Starlette
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Union[StarletteHTTPException, OptimadeHTTPException]
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def http_exception_handler(\n request: Request,\n exc: Union[StarletteHTTPException, OptimadeHTTPException],\n) -> JSONAPIResponse:\n \"\"\"Handle a general HTTP Exception from Starlette\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.not_implemented_handler","title":"not_implemented_handler(request, exc)
","text":"Handle a standard NotImplementedError Python exception
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
NotImplementedError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def not_implemented_handler(\n request: Request, exc: NotImplementedError\n) -> JSONAPIResponse:\n \"\"\"Handle a standard NotImplementedError Python exception\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n status = 501\n title = \"NotImplementedError\"\n detail = str(exc)\n error = OptimadeError(detail=detail, status=status, title=title)\n return general_exception(request, exc, status_code=status, errors=[error])\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.request_validation_exception_handler","title":"request_validation_exception_handler(request, exc)
","text":"Handle a request validation error from FastAPI
RequestValidationError
is a specialization of a general pydantic ValidationError
. Pass-through directly to general_exception()
.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
RequestValidationError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def request_validation_exception_handler(\n request: Request, exc: RequestValidationError\n) -> JSONAPIResponse:\n \"\"\"Handle a request validation error from FastAPI\n\n `RequestValidationError` is a specialization of a general pydantic `ValidationError`.\n Pass-through directly to [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.validation_exception_handler","title":"validation_exception_handler(request, exc)
","text":"Handle a general pydantic validation error
The pydantic ValidationError
usually contains a list of errors, this function extracts them and wraps them in the OPTIMADE specific error resource.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
ValidationError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def validation_exception_handler(\n request: Request, exc: ValidationError\n) -> JSONAPIResponse:\n \"\"\"Handle a general pydantic validation error\n\n The pydantic `ValidationError` usually contains a list of errors,\n this function extracts them and wraps them in the OPTIMADE specific error resource.\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n status = 500\n title = \"ValidationError\"\n errors = set()\n for error in exc.errors():\n pointer = \"/\" + \"/\".join([str(_) for _ in error[\"loc\"]])\n source = ErrorSource(pointer=pointer)\n code = error[\"type\"]\n detail = error[\"msg\"]\n errors.add(\n OptimadeError(\n detail=detail, status=status, title=title, source=source, code=code\n )\n )\n return general_exception(request, exc, status_code=status, errors=list(errors))\n
"},{"location":"api_reference/server/exceptions/","title":"exceptions","text":"Reproduced imports from optimade.exceptions
for backwards-compatibility.
BadRequest
","text":" Bases: OptimadeHTTPException
400 Bad Request
Source code inoptimade/exceptions.py
class BadRequest(OptimadeHTTPException):\n \"\"\"400 Bad Request\"\"\"\n\n status_code: int = 400\n title: str = \"Bad Request\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.Forbidden","title":"Forbidden
","text":" Bases: OptimadeHTTPException
403 Forbidden
Source code inoptimade/exceptions.py
class Forbidden(OptimadeHTTPException):\n \"\"\"403 Forbidden\"\"\"\n\n status_code: int = 403\n title: str = \"Forbidden\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.InternalServerError","title":"InternalServerError
","text":" Bases: OptimadeHTTPException
500 Internal Server Error
Source code inoptimade/exceptions.py
class InternalServerError(OptimadeHTTPException):\n \"\"\"500 Internal Server Error\"\"\"\n\n status_code: int = 500\n title: str = \"Internal Server Error\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.NotFound","title":"NotFound
","text":" Bases: OptimadeHTTPException
404 Not Found
Source code inoptimade/exceptions.py
class NotFound(OptimadeHTTPException):\n \"\"\"404 Not Found\"\"\"\n\n status_code: int = 404\n title: str = \"Not Found\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.NotImplementedResponse","title":"NotImplementedResponse
","text":" Bases: OptimadeHTTPException
501 Not Implemented
Source code inoptimade/exceptions.py
class NotImplementedResponse(OptimadeHTTPException):\n \"\"\"501 Not Implemented\"\"\"\n\n status_code: int = 501\n title: str = \"Not Implemented\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.OptimadeHTTPException","title":"OptimadeHTTPException
","text":" Bases: Exception
, ABC
This abstract class can be subclassed to define HTTP responses with the desired status codes, and detailed error strings to represent in the JSON:API error response.
This class closely follows the starlette.HTTPException
without requiring it as a dependency, so that such errors can also be raised from within client code.
Attributes:
Name Type Descriptionstatus_code
int
The HTTP status code accompanying this exception.
title
str
A descriptive title for this exception.
detail
Optional[str]
An optional string containing the details of the error.
Source code inoptimade/exceptions.py
class OptimadeHTTPException(Exception, ABC):\n \"\"\"This abstract class can be subclassed to define\n HTTP responses with the desired status codes, and\n detailed error strings to represent in the JSON:API\n error response.\n\n This class closely follows the `starlette.HTTPException` without\n requiring it as a dependency, so that such errors can also be\n raised from within client code.\n\n Attributes:\n status_code: The HTTP status code accompanying this exception.\n title: A descriptive title for this exception.\n detail: An optional string containing the details of the error.\n\n \"\"\"\n\n status_code: int\n title: str\n detail: Optional[str] = None\n headers: Optional[dict[str, Any]] = None\n\n def __init__(\n self, detail: Optional[str] = None, headers: Optional[dict] = None\n ) -> None:\n if self.status_code is None:\n raise AttributeError(\n \"HTTPException class {self.__class__.__name__} is missing required `status_code` attribute.\"\n )\n self.detail = detail\n self.headers = headers\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else self.__repr__()\n\n def __repr__(self) -> str:\n class_name = self.__class__.__name__\n return f\"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.UnprocessableEntity","title":"UnprocessableEntity
","text":" Bases: OptimadeHTTPException
422 Unprocessable Entity
Source code inoptimade/exceptions.py
class UnprocessableEntity(OptimadeHTTPException):\n \"\"\"422 Unprocessable Entity\"\"\"\n\n status_code: int = 422\n title: str = \"Unprocessable Entity\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.VersionNotSupported","title":"VersionNotSupported
","text":" Bases: OptimadeHTTPException
553 Version Not Supported
Source code inoptimade/exceptions.py
class VersionNotSupported(OptimadeHTTPException):\n \"\"\"553 Version Not Supported\"\"\"\n\n status_code: int = 553\n title: str = \"Version Not Supported\"\n
"},{"location":"api_reference/server/logger/","title":"logger","text":"Logging to both file and terminal
"},{"location":"api_reference/server/main/","title":"main","text":"The OPTIMADE server
The server is based on MongoDB, using either pymongo
or mongomock
.
This is an example implementation with example data. To implement your own server see the documentation at https://optimade.org/optimade-python-tools.
"},{"location":"api_reference/server/main/#optimade.server.main.add_major_version_base_url","title":"add_major_version_base_url(app)
","text":"Add mandatory vMajor endpoints, i.e. all except versions.
Source code inoptimade/server/main.py
def add_major_version_base_url(app: FastAPI):\n \"\"\"Add mandatory vMajor endpoints, i.e. all except versions.\"\"\"\n for endpoint in (info, links, references, structures, landing):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[\"major\"])\n
"},{"location":"api_reference/server/main/#optimade.server.main.add_optional_versioned_base_urls","title":"add_optional_versioned_base_urls(app)
","text":"Add the following OPTIONAL prefixes/base URLs to server:
/vMajor.Minor\n /vMajor.Minor.Patch\n
Source code in optimade/server/main.py
def add_optional_versioned_base_urls(app: FastAPI):\n \"\"\"Add the following OPTIONAL prefixes/base URLs to server:\n ```\n /vMajor.Minor\n /vMajor.Minor.Patch\n ```\n \"\"\"\n for version in (\"minor\", \"patch\"):\n for endpoint in (info, links, references, structures, landing):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[version])\n
"},{"location":"api_reference/server/main/#optimade.server.main.lifespan","title":"lifespan(app)
async
","text":"Add dynamic endpoints on startup.
Source code inoptimade/server/main.py
@asynccontextmanager # type: ignore[arg-type]\nasync def lifespan(app: FastAPI):\n \"\"\"Add dynamic endpoints on startup.\"\"\"\n # Add API endpoints for MANDATORY base URL `/vMAJOR`\n add_major_version_base_url(app)\n # Add API endpoints for OPTIONAL base URLs `/vMAJOR.MINOR` and `/vMAJOR.MINOR.PATCH`\n add_optional_versioned_base_urls(app)\n\n # Yield so that the app can start\n yield\n
"},{"location":"api_reference/server/main_index/","title":"main_index","text":"The OPTIMADE Index Meta-Database server
The server is based on MongoDB, using either pymongo
or mongomock
.
This is an example implementation with example data. To implement your own index meta-database server see the documentation at https://optimade.org/optimade-python-tools.
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.add_major_version_base_url","title":"add_major_version_base_url(app)
","text":"Add mandatory endpoints to /vMAJOR
base URL.
optimade/server/main_index.py
def add_major_version_base_url(app: FastAPI):\n \"\"\"Add mandatory endpoints to `/vMAJOR` base URL.\"\"\"\n for endpoint in (index_info, links):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[\"major\"])\n
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.add_optional_versioned_base_urls","title":"add_optional_versioned_base_urls(app)
","text":"Add the following OPTIONAL prefixes/base URLs to server:
/vMajor.Minor\n /vMajor.Minor.Patch\n
Source code in optimade/server/main_index.py
def add_optional_versioned_base_urls(app: FastAPI):\n \"\"\"Add the following OPTIONAL prefixes/base URLs to server:\n ```\n /vMajor.Minor\n /vMajor.Minor.Patch\n ```\n \"\"\"\n for version in (\"minor\", \"patch\"):\n app.include_router(index_info.router, prefix=BASE_URL_PREFIXES[version])\n app.include_router(links.router, prefix=BASE_URL_PREFIXES[version])\n
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.lifespan","title":"lifespan(app)
async
","text":"Add dynamic endpoints and adjust config on startup.
Source code inoptimade/server/main_index.py
@asynccontextmanager # type: ignore[arg-type]\nasync def lifespan(app: FastAPI):\n \"\"\"Add dynamic endpoints and adjust config on startup.\"\"\"\n CONFIG.is_index = True\n # Add API endpoints for MANDATORY base URL `/vMAJOR`\n add_major_version_base_url(app)\n # Add API endpoints for OPTIONAL base URLs `/vMAJOR.MINOR` and `/vMAJOR.MINOR.PATCH`\n add_optional_versioned_base_urls(app)\n\n # Yield so that the app can start\n yield\n
"},{"location":"api_reference/server/middleware/","title":"middleware","text":"Custom ASGI app middleware.
These middleware are based on Starlette's BaseHTTPMiddleware
. See the specific Starlette documentation page for more information on it's middleware implementation.
OPTIMADE_MIDDLEWARE: Iterable[BaseHTTPMiddleware] = (EnsureQueryParamIntegrity, CheckWronglyVersionedBaseUrls, HandleApiHint, AddWarnings)
module-attribute
","text":"A tuple of all the middleware classes that implement certain required features of the OPTIMADE specification, e.g. warnings and URL versioning.
Note
The order in which middleware is added to an application matters.
As discussed in the docstring of AddWarnings
, this middleware is the final entry to this list so that it is the first to be applied by the server. Any other middleware should therefore be added before iterating through this variable. This is the opposite way around to the example in the Starlette documentation which initialises the application with a pre-built middleware list in the reverse order to OPTIMADE_MIDDLEWARE
.
To use this variable in FastAPI app code after initialisation:
from fastapi import FastAPI\napp = FastAPI()\nfor middleware in OPTIMADE_MIDDLEWARE:\n app.add_middleware(middleware)\n
Alternatively, to use this variable on initialisation:
from fastapi import FastAPI\nfrom starlette.middleware import Middleware\napp = FastAPI(\n ...,\n middleware=[Middleware(m) for m in reversed(OPTIMADE_MIDDLEWARE)]\n)\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings","title":"AddWarnings
","text":" Bases: BaseHTTPMiddleware
Add OptimadeWarning
s to the response.
All sub-classes of OptimadeWarning
will also be added to the response's meta.warnings
list.
By overriding the warnings.showwarning()
function with the showwarning
method, all usages of warnings.warn()
will result in the regular printing of the warning message to stderr
, but also its addition to an in-memory list of warnings. This middleware will, after the URL request has been handled, add the list of accumulated warnings to the JSON response under the meta.warnings
field.
To make sure the last part happens correctly and a Starlette StreamingResponse
is returned, as is expected from a BaseHTTPMiddleware
sub-class, one is instantiated with the updated Content-Length
header, as well as making sure the response's body content is actually streamable, by breaking it down into chunks of the original response's chunk size.
Important
It is recommended to add this middleware as the last one to your application.
This is to ensure it is invoked first, updating warnings.showwarning()
and catching all warnings that should be added to the response.
This can be achieved by applying AddWarnings
after all other middleware with the .add_middleware()
method, or by initialising the app with a middleware list in which AddWarnings
appears first. More information can be found in the docstring of OPTIMADE_MIDDLEWARE
.
Attributes:
Name Type Description_warnings
List[Warnings]
List of Warnings
added through usages of warnings.warn()
via showwarning
.
optimade/server/middleware.py
class AddWarnings(BaseHTTPMiddleware):\n \"\"\"\n Add [`OptimadeWarning`][optimade.warnings.OptimadeWarning]s to the response.\n\n All sub-classes of [`OptimadeWarning`][optimade.warnings.OptimadeWarning]\n will also be added to the response's\n [`meta.warnings`][optimade.models.optimade_json.ResponseMeta.warnings] list.\n\n By overriding the `warnings.showwarning()` function with the\n [`showwarning` method][optimade.server.middleware.AddWarnings.showwarning],\n all usages of `warnings.warn()` will result in the regular printing of the\n warning message to `stderr`, but also its addition to an in-memory list of\n warnings.\n This middleware will, after the URL request has been handled, add the list of\n accumulated warnings to the JSON response under the\n [`meta.warnings`][optimade.models.optimade_json.ResponseMeta.warnings] field.\n\n To make sure the last part happens correctly and a Starlette `StreamingResponse`\n is returned, as is expected from a `BaseHTTPMiddleware` sub-class, one is\n instantiated with the updated `Content-Length` header, as well as making sure\n the response's body content is actually streamable, by breaking it down into\n chunks of the original response's chunk size.\n\n !!! warning \"Important\"\n It is **recommended** to add this middleware as the _last one_ to your application.\n\n This is to ensure it is invoked _first_, updating `warnings.showwarning()` and\n catching all warnings that should be added to the response.\n\n This can be achieved by applying `AddWarnings` _after_ all\n other middleware with the `.add_middleware()` method, or by\n initialising the app with a middleware list in which `AddWarnings`\n appears _first_. More information can be found in the docstring of\n [`OPTIMADE_MIDDLEWARE`][optimade.server.middleware.OPTIMADE_MIDDLEWARE].\n\n Attributes:\n _warnings (List[Warnings]): List of [`Warnings`][optimade.models.optimade_json.Warnings]\n added through usages of `warnings.warn()` via [`showwarning`][optimade.server.middleware.AddWarnings.showwarning].\n\n \"\"\"\n\n _warnings: list[Warnings]\n\n def showwarning(\n self,\n message: Union[Warning, str],\n category: type[Warning],\n filename: str,\n lineno: int,\n file: Optional[TextIO] = None,\n line: Optional[str] = None,\n ) -> None:\n \"\"\"\n Hook to write a warning to a file using the built-in `warnings` lib.\n\n In [the documentation](https://docs.python.org/3/library/warnings.html)\n for the built-in `warnings` library, there are a few recommended ways of\n customizing the printing of warning messages.\n\n This method can override the `warnings.showwarning` function,\n which is called as part of the `warnings` library's workflow to print\n warning messages, e.g., when using `warnings.warn()`.\n Originally, it prints warning messages to `stderr`.\n This method will also print warning messages to `stderr` by calling\n `warnings._showwarning_orig()` or `warnings._showwarnmsg_impl()`.\n The first function will be called if the issued warning is not recognized\n as an [`OptimadeWarning`][optimade.warnings.OptimadeWarning].\n This is equivalent to \"standard behaviour\".\n The second function will be called _after_ an\n [`OptimadeWarning`][optimade.warnings.OptimadeWarning] has been handled.\n\n An [`OptimadeWarning`][optimade.warnings.OptimadeWarning] will be\n translated into an OPTIMADE Warnings JSON object in accordance with\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#json-response-schema-common-fields).\n This process is similar to the [Exception handlers][optimade.server.exception_handlers].\n\n Parameters:\n message: The `Warning` object to show and possibly handle.\n category: `Warning` type being warned about. This amounts to `type(message)`.\n filename: Name of the file, where the warning was issued.\n lineno: Line number in the file, where the warning was issued.\n file: A file-like object to which the warning should be written.\n line: Source content of the line that issued the warning.\n\n \"\"\"\n assert isinstance(\n message, Warning\n ), \"'message' is expected to be a Warning or subclass thereof.\"\n\n if not isinstance(message, OptimadeWarning):\n # If the Warning is not an OptimadeWarning or subclass thereof,\n # use the regular 'showwarning' function.\n warnings._showwarning_orig(message, category, filename, lineno, file, line) # type: ignore[attr-defined]\n return\n\n if isinstance(message, LocalOptimadeWarning):\n return\n\n # Format warning\n try:\n title = str(message.title)\n except AttributeError:\n title = str(message.__class__.__name__)\n\n try:\n detail = str(message.detail)\n except AttributeError:\n detail = str(message)\n\n if CONFIG.debug:\n if line is None:\n # All this is taken directly from the warnings library.\n # See 'warnings._formatwarnmsg_impl()' for the original code.\n try:\n import linecache\n\n line = linecache.getline(filename, lineno)\n except Exception:\n # When a warning is logged during Python shutdown, linecache\n # and the import machinery don't work anymore\n line = None\n meta = {\n \"filename\": filename,\n \"lineno\": lineno,\n }\n if line:\n meta[\"line\"] = line.strip()\n\n if CONFIG.debug:\n new_warning = Warnings(title=title, detail=detail, meta=meta)\n else:\n new_warning = Warnings(title=title, detail=detail)\n\n # Add new warning to self._warnings\n self._warnings.append(new_warning.model_dump(exclude_unset=True))\n\n # Show warning message as normal in sys.stderr\n warnings._showwarnmsg_impl( # type: ignore[attr-defined]\n warnings.WarningMessage(message, category, filename, lineno, file, line)\n )\n\n @staticmethod\n def chunk_it_up(content: Union[str, bytes], chunk_size: int) -> Generator:\n \"\"\"Return generator for string in chunks of size `chunk_size`.\n\n Parameters:\n content: String or bytes content to separate into chunks.\n chunk_size: The size of the chunks, i.e. the length of the string-chunks.\n\n Returns:\n A Python generator to be converted later to an `asyncio` generator.\n\n \"\"\"\n if chunk_size <= 0:\n chunk_size = 1\n return (content[i : chunk_size + i] for i in range(0, len(content), chunk_size))\n\n async def dispatch(self, request: Request, call_next):\n self._warnings = []\n\n warnings.simplefilter(action=\"default\", category=OptimadeWarning)\n warnings.showwarning = self.showwarning\n\n response = await call_next(request)\n\n status = response.status_code\n headers = response.headers\n media_type = response.media_type\n background = response.background\n charset = response.charset\n\n body = b\"\"\n chunk_size = 0\n async for chunk in response.body_iterator:\n chunk_size = chunk_size or len(chunk)\n if not isinstance(chunk, bytes):\n chunk = chunk.encode(charset)\n body += chunk\n body_str = body.decode(charset)\n\n if self._warnings:\n response = json.loads(body_str)\n response.get(\"meta\", {})[\"warnings\"] = self._warnings\n body_str = json.dumps(response)\n if \"content-length\" in headers:\n headers[\"content-length\"] = str(len(body_str))\n\n response = StreamingResponse(\n content=self.chunk_it_up(body_str, chunk_size),\n status_code=status,\n headers=headers,\n media_type=media_type,\n background=background,\n )\n\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings.chunk_it_up","title":"chunk_it_up(content, chunk_size)
staticmethod
","text":"Return generator for string in chunks of size chunk_size
.
Parameters:
Name Type Description Defaultcontent
Union[str, bytes]
String or bytes content to separate into chunks.
requiredchunk_size
int
The size of the chunks, i.e. the length of the string-chunks.
requiredReturns:
Type DescriptionGenerator
A Python generator to be converted later to an asyncio
generator.
optimade/server/middleware.py
@staticmethod\ndef chunk_it_up(content: Union[str, bytes], chunk_size: int) -> Generator:\n \"\"\"Return generator for string in chunks of size `chunk_size`.\n\n Parameters:\n content: String or bytes content to separate into chunks.\n chunk_size: The size of the chunks, i.e. the length of the string-chunks.\n\n Returns:\n A Python generator to be converted later to an `asyncio` generator.\n\n \"\"\"\n if chunk_size <= 0:\n chunk_size = 1\n return (content[i : chunk_size + i] for i in range(0, len(content), chunk_size))\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings.showwarning","title":"showwarning(message, category, filename, lineno, file=None, line=None)
","text":"Hook to write a warning to a file using the built-in warnings
lib.
In the documentation for the built-in warnings
library, there are a few recommended ways of customizing the printing of warning messages.
This method can override the warnings.showwarning
function, which is called as part of the warnings
library's workflow to print warning messages, e.g., when using warnings.warn()
. Originally, it prints warning messages to stderr
. This method will also print warning messages to stderr
by calling warnings._showwarning_orig()
or warnings._showwarnmsg_impl()
. The first function will be called if the issued warning is not recognized as an OptimadeWarning
. This is equivalent to \"standard behaviour\". The second function will be called after an OptimadeWarning
has been handled.
An OptimadeWarning
will be translated into an OPTIMADE Warnings JSON object in accordance with the specification. This process is similar to the Exception handlers.
Parameters:
Name Type Description Defaultmessage
Union[Warning, str]
The Warning
object to show and possibly handle.
category
type[Warning]
Warning
type being warned about. This amounts to type(message)
.
filename
str
Name of the file, where the warning was issued.
requiredlineno
int
Line number in the file, where the warning was issued.
requiredfile
Optional[TextIO]
A file-like object to which the warning should be written.
None
line
Optional[str]
Source content of the line that issued the warning.
None
Source code in optimade/server/middleware.py
def showwarning(\n self,\n message: Union[Warning, str],\n category: type[Warning],\n filename: str,\n lineno: int,\n file: Optional[TextIO] = None,\n line: Optional[str] = None,\n) -> None:\n \"\"\"\n Hook to write a warning to a file using the built-in `warnings` lib.\n\n In [the documentation](https://docs.python.org/3/library/warnings.html)\n for the built-in `warnings` library, there are a few recommended ways of\n customizing the printing of warning messages.\n\n This method can override the `warnings.showwarning` function,\n which is called as part of the `warnings` library's workflow to print\n warning messages, e.g., when using `warnings.warn()`.\n Originally, it prints warning messages to `stderr`.\n This method will also print warning messages to `stderr` by calling\n `warnings._showwarning_orig()` or `warnings._showwarnmsg_impl()`.\n The first function will be called if the issued warning is not recognized\n as an [`OptimadeWarning`][optimade.warnings.OptimadeWarning].\n This is equivalent to \"standard behaviour\".\n The second function will be called _after_ an\n [`OptimadeWarning`][optimade.warnings.OptimadeWarning] has been handled.\n\n An [`OptimadeWarning`][optimade.warnings.OptimadeWarning] will be\n translated into an OPTIMADE Warnings JSON object in accordance with\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#json-response-schema-common-fields).\n This process is similar to the [Exception handlers][optimade.server.exception_handlers].\n\n Parameters:\n message: The `Warning` object to show and possibly handle.\n category: `Warning` type being warned about. This amounts to `type(message)`.\n filename: Name of the file, where the warning was issued.\n lineno: Line number in the file, where the warning was issued.\n file: A file-like object to which the warning should be written.\n line: Source content of the line that issued the warning.\n\n \"\"\"\n assert isinstance(\n message, Warning\n ), \"'message' is expected to be a Warning or subclass thereof.\"\n\n if not isinstance(message, OptimadeWarning):\n # If the Warning is not an OptimadeWarning or subclass thereof,\n # use the regular 'showwarning' function.\n warnings._showwarning_orig(message, category, filename, lineno, file, line) # type: ignore[attr-defined]\n return\n\n if isinstance(message, LocalOptimadeWarning):\n return\n\n # Format warning\n try:\n title = str(message.title)\n except AttributeError:\n title = str(message.__class__.__name__)\n\n try:\n detail = str(message.detail)\n except AttributeError:\n detail = str(message)\n\n if CONFIG.debug:\n if line is None:\n # All this is taken directly from the warnings library.\n # See 'warnings._formatwarnmsg_impl()' for the original code.\n try:\n import linecache\n\n line = linecache.getline(filename, lineno)\n except Exception:\n # When a warning is logged during Python shutdown, linecache\n # and the import machinery don't work anymore\n line = None\n meta = {\n \"filename\": filename,\n \"lineno\": lineno,\n }\n if line:\n meta[\"line\"] = line.strip()\n\n if CONFIG.debug:\n new_warning = Warnings(title=title, detail=detail, meta=meta)\n else:\n new_warning = Warnings(title=title, detail=detail)\n\n # Add new warning to self._warnings\n self._warnings.append(new_warning.model_dump(exclude_unset=True))\n\n # Show warning message as normal in sys.stderr\n warnings._showwarnmsg_impl( # type: ignore[attr-defined]\n warnings.WarningMessage(message, category, filename, lineno, file, line)\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.CheckWronglyVersionedBaseUrls","title":"CheckWronglyVersionedBaseUrls
","text":" Bases: BaseHTTPMiddleware
If a non-supported versioned base URL is supplied return 553 Version Not Supported
.
optimade/server/middleware.py
class CheckWronglyVersionedBaseUrls(BaseHTTPMiddleware):\n \"\"\"If a non-supported versioned base URL is supplied return `553 Version Not Supported`.\"\"\"\n\n @staticmethod\n def check_url(url: StarletteURL):\n \"\"\"Check URL path for versioned part.\n\n Parameters:\n url: A complete urllib-parsed raw URL.\n\n Raises:\n VersionNotSupported: If the URL represents an OPTIMADE versioned base URL\n and the version part is not supported by the implementation.\n\n \"\"\"\n base_url = get_base_url(url)\n optimade_path = f\"{url.scheme}://{url.netloc}{url.path}\"[len(base_url) :]\n match = re.match(r\"^(?P<version>/v[0-9]+(\\.[0-9]+){0,2}).*\", optimade_path)\n if match is not None:\n if match.group(\"version\") not in BASE_URL_PREFIXES.values():\n raise VersionNotSupported(\n detail=(\n f\"The parsed versioned base URL {match.group('version')!r} from \"\n f\"{url} is not supported by this implementation. \"\n f\"Supported versioned base URLs are: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n\n async def dispatch(self, request: Request, call_next):\n if request.url.path:\n self.check_url(request.url)\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.CheckWronglyVersionedBaseUrls.check_url","title":"check_url(url)
staticmethod
","text":"Check URL path for versioned part.
Parameters:
Name Type Description Defaulturl
URL
A complete urllib-parsed raw URL.
requiredRaises:
Type DescriptionVersionNotSupported
If the URL represents an OPTIMADE versioned base URL and the version part is not supported by the implementation.
Source code inoptimade/server/middleware.py
@staticmethod\ndef check_url(url: StarletteURL):\n \"\"\"Check URL path for versioned part.\n\n Parameters:\n url: A complete urllib-parsed raw URL.\n\n Raises:\n VersionNotSupported: If the URL represents an OPTIMADE versioned base URL\n and the version part is not supported by the implementation.\n\n \"\"\"\n base_url = get_base_url(url)\n optimade_path = f\"{url.scheme}://{url.netloc}{url.path}\"[len(base_url) :]\n match = re.match(r\"^(?P<version>/v[0-9]+(\\.[0-9]+){0,2}).*\", optimade_path)\n if match is not None:\n if match.group(\"version\") not in BASE_URL_PREFIXES.values():\n raise VersionNotSupported(\n detail=(\n f\"The parsed versioned base URL {match.group('version')!r} from \"\n f\"{url} is not supported by this implementation. \"\n f\"Supported versioned base URLs are: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.EnsureQueryParamIntegrity","title":"EnsureQueryParamIntegrity
","text":" Bases: BaseHTTPMiddleware
Ensure all query parameters are followed by an equal sign (=
).
optimade/server/middleware.py
class EnsureQueryParamIntegrity(BaseHTTPMiddleware):\n \"\"\"Ensure all query parameters are followed by an equal sign (`=`).\"\"\"\n\n @staticmethod\n def check_url(url_query: str) -> set:\n \"\"\"Check parsed URL query part for parameters not followed by `=`.\n\n URL query parameters are considered to be split by ampersand (`&`)\n and semi-colon (`;`).\n\n Parameters:\n url_query: The raw urllib-parsed query part.\n\n Raises:\n BadRequest: If a query parameter does not come with a value.\n\n Returns:\n The set of individual query parameters and their values.\n\n This is mainly for testing and not actually neeeded by the middleware,\n since if the URL exhibits an invalid query part a `400 Bad Request`\n response will be returned.\n\n \"\"\"\n queries_amp = set(url_query.split(\"&\"))\n queries = set()\n for query in queries_amp:\n queries.update(set(query.split(\";\")))\n for query in queries:\n if \"=\" not in query and query != \"\":\n raise BadRequest(\n detail=\"A query parameter without an equal sign (=) is not supported by this server\"\n )\n return queries # Useful for testing\n\n async def dispatch(self, request: Request, call_next):\n parsed_url = urllib.parse.urlsplit(str(request.url))\n if parsed_url.query:\n self.check_url(parsed_url.query)\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.EnsureQueryParamIntegrity.check_url","title":"check_url(url_query)
staticmethod
","text":"Check parsed URL query part for parameters not followed by =
.
URL query parameters are considered to be split by ampersand (&
) and semi-colon (;
).
Parameters:
Name Type Description Defaulturl_query
str
The raw urllib-parsed query part.
requiredRaises:
Type DescriptionBadRequest
If a query parameter does not come with a value.
Returns:
Type Descriptionset
The set of individual query parameters and their values.
set
This is mainly for testing and not actually neeeded by the middleware,
set
since if the URL exhibits an invalid query part a 400 Bad Request
set
response will be returned.
Source code inoptimade/server/middleware.py
@staticmethod\ndef check_url(url_query: str) -> set:\n \"\"\"Check parsed URL query part for parameters not followed by `=`.\n\n URL query parameters are considered to be split by ampersand (`&`)\n and semi-colon (`;`).\n\n Parameters:\n url_query: The raw urllib-parsed query part.\n\n Raises:\n BadRequest: If a query parameter does not come with a value.\n\n Returns:\n The set of individual query parameters and their values.\n\n This is mainly for testing and not actually neeeded by the middleware,\n since if the URL exhibits an invalid query part a `400 Bad Request`\n response will be returned.\n\n \"\"\"\n queries_amp = set(url_query.split(\"&\"))\n queries = set()\n for query in queries_amp:\n queries.update(set(query.split(\";\")))\n for query in queries:\n if \"=\" not in query and query != \"\":\n raise BadRequest(\n detail=\"A query parameter without an equal sign (=) is not supported by this server\"\n )\n return queries # Useful for testing\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint","title":"HandleApiHint
","text":" Bases: BaseHTTPMiddleware
Handle api_hint
query parameter.
optimade/server/middleware.py
class HandleApiHint(BaseHTTPMiddleware):\n \"\"\"Handle `api_hint` query parameter.\"\"\"\n\n @staticmethod\n def handle_api_hint(api_hint: list[str]) -> Union[None, str]:\n \"\"\"Handle `api_hint` parameter value.\n\n There are several scenarios that can play out, when handling the `api_hint`\n query parameter:\n\n If several `api_hint` query parameters have been used, or a \"standard\" JSON\n list (`,`-separated value) has been supplied, a warning will be added to the\n response and the `api_hint` query parameter will not be applied.\n\n If the passed value does not comply with the rules set out in\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation),\n a warning will be added to the response and the `api_hint` query parameter\n will not be applied.\n\n If the value is part of the implementation's accepted versioned base URLs,\n it will be returned as is.\n\n If the value represents a major version that is newer than what is supported\n by the implementation, a `553 Version Not Supported` response will be returned,\n as is stated by [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation).\n\n On the other hand, if the value represents a major version equal to or lower\n than the implementation's supported major version, then the implementation's\n supported major version will be returned and tried for the request.\n\n Parameters:\n api_hint: The urllib-parsed query parameter value for `api_hint`.\n\n Raises:\n VersionNotSupported: If the requested major version is newer than the\n supported major version of the implementation.\n\n Returns:\n Either a valid `api_hint` value or `None`.\n\n \"\"\"\n # Try to split by `,` if value is provided once, but in JSON-type \"list\" format\n _api_hint = []\n for value in api_hint:\n values = value.split(\",\")\n _api_hint.extend(values)\n\n if len(_api_hint) > 1:\n warnings.warn(\n TooManyValues(\n detail=\"`api_hint` should only be supplied once, with a single value.\"\n )\n )\n return None\n\n api_hint_str: str = f\"/{_api_hint[0]}\"\n if re.match(r\"^/v[0-9]+(\\.[0-9]+)?$\", api_hint_str) is None:\n warnings.warn(\n FieldValueNotRecognized(\n detail=f\"{api_hint_str[1:]!r} is not recognized as a valid `api_hint` value.\"\n )\n )\n return None\n\n if api_hint_str in BASE_URL_PREFIXES.values():\n return api_hint_str\n\n major_api_hint = int(re.findall(r\"/v([0-9]+)\", api_hint_str)[0])\n major_implementation = int(BASE_URL_PREFIXES[\"major\"][len(\"/v\") :])\n\n if major_api_hint <= major_implementation:\n # If less than:\n # Use the current implementation in hope that it can still handle older requests\n #\n # If equal:\n # Go to /v<MAJOR>, since this should point to the latest available\n return BASE_URL_PREFIXES[\"major\"]\n\n # Let's not try to handle a request for a newer major version\n raise VersionNotSupported(\n detail=(\n f\"The provided `api_hint` ({api_hint_str[1:]!r}) is not supported by this implementation. \"\n f\"Supported versions include: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n\n @staticmethod\n def is_versioned_base_url(url: str) -> bool:\n \"\"\"Determine whether a request is for a versioned base URL.\n\n First, simply check whether a `/vMAJOR(.MINOR.PATCH)` part exists in the URL.\n If not, return `False`, else, remove unversioned base URL from the URL and check again.\n Return `bool` of final result.\n\n Parameters:\n url: The full URL to check.\n\n Returns:\n Whether or not the full URL represents an OPTIMADE versioned base URL.\n\n \"\"\"\n if not re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url):\n return False\n\n base_url = get_base_url(url)\n return bool(re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url[len(base_url) :]))\n\n async def dispatch(self, request: Request, call_next):\n parsed_query = urllib.parse.parse_qs(request.url.query, keep_blank_values=True)\n\n if \"api_hint\" in parsed_query:\n if self.is_versioned_base_url(str(request.url)):\n warnings.warn(\n QueryParamNotUsed(\n detail=(\n \"`api_hint` provided with value{:s} '{:s}' for a versioned base URL. \"\n \"In accordance with the specification, this will not be handled by \"\n \"the implementation.\".format(\n \"s\" if len(parsed_query[\"api_hint\"]) > 1 else \"\",\n \"', '\".join(parsed_query[\"api_hint\"]),\n )\n )\n )\n )\n else:\n from optimade.server.routers.utils import get_base_url\n\n version_path = self.handle_api_hint(parsed_query[\"api_hint\"])\n\n if version_path:\n base_url = get_base_url(request.url)\n\n new_request = (\n f\"{base_url}{version_path}{str(request.url)[len(base_url):]}\"\n )\n url = urllib.parse.urlsplit(new_request)\n q = \"&\".join(\n [\n f\"{key}={value}\"\n for key, value in urllib.parse.parse_qsl(\n url.query, keep_blank_values=True\n )\n if key != \"api_hint\"\n ]\n )\n\n return RedirectResponse(\n request.url.replace(path=url.path, query=q),\n headers=request.headers,\n )\n # This is the non-URL changing solution:\n #\n # scope = request.scope\n # scope[\"path\"] = path\n # request = Request(scope=scope, receive=request.receive, send=request._send)\n\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint.handle_api_hint","title":"handle_api_hint(api_hint)
staticmethod
","text":"Handle api_hint
parameter value.
There are several scenarios that can play out, when handling the api_hint
query parameter:
If several api_hint
query parameters have been used, or a \"standard\" JSON list (,
-separated value) has been supplied, a warning will be added to the response and the api_hint
query parameter will not be applied.
If the passed value does not comply with the rules set out in the specification, a warning will be added to the response and the api_hint
query parameter will not be applied.
If the value is part of the implementation's accepted versioned base URLs, it will be returned as is.
If the value represents a major version that is newer than what is supported by the implementation, a 553 Version Not Supported
response will be returned, as is stated by the specification.
On the other hand, if the value represents a major version equal to or lower than the implementation's supported major version, then the implementation's supported major version will be returned and tried for the request.
Parameters:
Name Type Description Defaultapi_hint
list[str]
The urllib-parsed query parameter value for api_hint
.
Raises:
Type DescriptionVersionNotSupported
If the requested major version is newer than the supported major version of the implementation.
Returns:
Type DescriptionUnion[None, str]
Either a valid api_hint
value or None
.
optimade/server/middleware.py
@staticmethod\ndef handle_api_hint(api_hint: list[str]) -> Union[None, str]:\n \"\"\"Handle `api_hint` parameter value.\n\n There are several scenarios that can play out, when handling the `api_hint`\n query parameter:\n\n If several `api_hint` query parameters have been used, or a \"standard\" JSON\n list (`,`-separated value) has been supplied, a warning will be added to the\n response and the `api_hint` query parameter will not be applied.\n\n If the passed value does not comply with the rules set out in\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation),\n a warning will be added to the response and the `api_hint` query parameter\n will not be applied.\n\n If the value is part of the implementation's accepted versioned base URLs,\n it will be returned as is.\n\n If the value represents a major version that is newer than what is supported\n by the implementation, a `553 Version Not Supported` response will be returned,\n as is stated by [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation).\n\n On the other hand, if the value represents a major version equal to or lower\n than the implementation's supported major version, then the implementation's\n supported major version will be returned and tried for the request.\n\n Parameters:\n api_hint: The urllib-parsed query parameter value for `api_hint`.\n\n Raises:\n VersionNotSupported: If the requested major version is newer than the\n supported major version of the implementation.\n\n Returns:\n Either a valid `api_hint` value or `None`.\n\n \"\"\"\n # Try to split by `,` if value is provided once, but in JSON-type \"list\" format\n _api_hint = []\n for value in api_hint:\n values = value.split(\",\")\n _api_hint.extend(values)\n\n if len(_api_hint) > 1:\n warnings.warn(\n TooManyValues(\n detail=\"`api_hint` should only be supplied once, with a single value.\"\n )\n )\n return None\n\n api_hint_str: str = f\"/{_api_hint[0]}\"\n if re.match(r\"^/v[0-9]+(\\.[0-9]+)?$\", api_hint_str) is None:\n warnings.warn(\n FieldValueNotRecognized(\n detail=f\"{api_hint_str[1:]!r} is not recognized as a valid `api_hint` value.\"\n )\n )\n return None\n\n if api_hint_str in BASE_URL_PREFIXES.values():\n return api_hint_str\n\n major_api_hint = int(re.findall(r\"/v([0-9]+)\", api_hint_str)[0])\n major_implementation = int(BASE_URL_PREFIXES[\"major\"][len(\"/v\") :])\n\n if major_api_hint <= major_implementation:\n # If less than:\n # Use the current implementation in hope that it can still handle older requests\n #\n # If equal:\n # Go to /v<MAJOR>, since this should point to the latest available\n return BASE_URL_PREFIXES[\"major\"]\n\n # Let's not try to handle a request for a newer major version\n raise VersionNotSupported(\n detail=(\n f\"The provided `api_hint` ({api_hint_str[1:]!r}) is not supported by this implementation. \"\n f\"Supported versions include: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint.is_versioned_base_url","title":"is_versioned_base_url(url)
staticmethod
","text":"Determine whether a request is for a versioned base URL.
First, simply check whether a /vMAJOR(.MINOR.PATCH)
part exists in the URL. If not, return False
, else, remove unversioned base URL from the URL and check again. Return bool
of final result.
Parameters:
Name Type Description Defaulturl
str
The full URL to check.
requiredReturns:
Type Descriptionbool
Whether or not the full URL represents an OPTIMADE versioned base URL.
Source code inoptimade/server/middleware.py
@staticmethod\ndef is_versioned_base_url(url: str) -> bool:\n \"\"\"Determine whether a request is for a versioned base URL.\n\n First, simply check whether a `/vMAJOR(.MINOR.PATCH)` part exists in the URL.\n If not, return `False`, else, remove unversioned base URL from the URL and check again.\n Return `bool` of final result.\n\n Parameters:\n url: The full URL to check.\n\n Returns:\n Whether or not the full URL represents an OPTIMADE versioned base URL.\n\n \"\"\"\n if not re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url):\n return False\n\n base_url = get_base_url(url)\n return bool(re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url[len(base_url) :]))\n
"},{"location":"api_reference/server/query_params/","title":"query_params","text":""},{"location":"api_reference/server/query_params/#optimade.server.query_params.BaseQueryParams","title":"BaseQueryParams
","text":" Bases: ABC
A base class for query parameters that provides validation via the check_params
method.
Attributes:
Name Type Descriptionunsupported_params
list[str]
Any string parameter listed here will raise a warning when passed to the check_params methods. Useful for disabling optional OPTIMADE query parameters that are not implemented by the server, e.g., cursor-based pagination.
Source code inoptimade/server/query_params.py
class BaseQueryParams(ABC):\n \"\"\"A base class for query parameters that provides validation via the `check_params` method.\n\n Attributes:\n unsupported_params: Any string parameter listed here will raise a warning when passed to\n the check_params methods. Useful for disabling optional OPTIMADE query parameters that\n are not implemented by the server, e.g., cursor-based pagination.\n\n \"\"\"\n\n unsupported_params: list[str] = []\n\n def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.BaseQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.EntryListingQueryParams","title":"EntryListingQueryParams
","text":" Bases: BaseQueryParams
Common query params for all Entry listing endpoints.
Attributes:
Name Type Descriptionfilter
str
A filter string, in the format described in section API Filtering Format Specification of the specification.
response_format
str
The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification.
Example: http://example.com/v1/structures?response_format=xml
email_address
EmailStr
An email address of the user making the request. The email SHOULD be that of a person and not an automatic system.
Example: http://example.com/v1/structures?email_address=user@example.com
response_fields
str
A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present.
Example: http://example.com/v1/structures?response_fields=last_modified,nsites
sort
str
If supporting sortable queries, an implementation MUST use the sort
query parameter with format as specified by JSON API 1.0.
An implementation MAY support multiple sort fields for a single query. If it does, it again MUST conform to the JSON API 1.0 specification.
If an implementation supports sorting for an entry listing endpoint, then the /info/<entries>
endpoint MUST include, for each field name <fieldname>
in its data.properties.<fieldname>
response value that can be used for sorting, the key sortable
with value true
. If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the sortable
key or set it equal to false
for the specific field name. The set of field names, with sortable
equal to true
are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification. The field sortable
is in addition to each property description and other OPTIONAL fields. An example is shown in the section Entry Listing Info Endpoints.
page_limit
int
Sets a numerical limit on the number of entries returned. See JSON API 1.0. The API implementation MUST return no more than the number specified. It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden
-- MUST be returned). The default limit value is up to the API implementation to decide.
Example: http://example.com/optimade/v1/structures?page_limit=100
page_offset
int
RECOMMENDED for use with offset-based pagination: using page_offset
and page_limit
is RECOMMENDED.
Example: Skip 50 structures and fetch up to 100: /structures?page_offset=50&page_limit=100
.
page_number
int
RECOMMENDED for use with page-based pagination: using page_number
and page_limit
is RECOMMENDED. It is RECOMMENDED that the first page has number 1, i.e., that page_number
is 1-based.
Example: Fetch page 2 of up to 50 structures per page: /structures?page_number=2&page_limit=50
.
page_cursor
int
RECOMMENDED for use with cursor-based pagination: using page_cursor
and page_limit
is RECOMMENDED.
page_above
str
RECOMMENDED for use with value-based pagination: using page_above
/page_below
and page_limit
is RECOMMENDED.
Example: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing id
, so page_above
value refers to an id
value): /structures?page_above=4000&page_limit=100
.
page_below
str
RECOMMENDED for use with value-based pagination: using page_above
/page_below
and page_limit
is RECOMMENDED.
include
str
A server MAY implement the JSON API concept of returning compound documents by utilizing the include
query parameter as specified by JSON API 1.0.
All related resource objects MUST be returned as part of an array value for the top-level included
field, see the section JSON Response Schema: Common Fields.
The value of include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a 400 Bad Request
response MUST be made.
The default value for include
is references
. This means references
entries MUST always be included under the top-level field included
as default, since a server assumes if include
is not specified by a client in the request, it is still specified as include=references
. Note, if a client explicitly specifies include
and leaves out references
, references
resource objects MUST NOT be included under the top-level field included
, as per the definition of included
, see section JSON Response Schema: Common Fields.
Note: A query with the parameter include
set to the empty string means no related resource objects are to be returned under the top-level field included
.
api_hint
str
If the client provides the parameter, the value SHOULD have the format vMAJOR
or vMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends api_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.
optimade/server/query_params.py
class EntryListingQueryParams(BaseQueryParams):\n \"\"\"\n Common query params for all Entry listing endpoints.\n\n Attributes:\n filter (str): A filter string, in the format described in section API Filtering Format Specification of the specification.\n\n response_format (str): The output format requested (see section Response Format).\n Defaults to the format string 'json', which specifies the standard output format described in this specification.\n\n **Example**: `http://example.com/v1/structures?response_format=xml`\n\n email_address (EmailStr): An email address of the user making the request.\n The email SHOULD be that of a person and not an automatic system.\n\n **Example**: `http://example.com/v1/structures?email_address=user@example.com`\n\n response_fields (str): A comma-delimited set of fields to be provided in the output.\n If provided, these fields MUST be returned along with the REQUIRED fields.\n Other OPTIONAL fields MUST NOT be returned when this parameter is present.\n\n **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites`\n\n sort (str): If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified\n by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\n An implementation MAY support multiple sort fields for a single query.\n If it does, it again MUST conform to the JSON API 1.0 specification.\n\n If an implementation supports sorting for an entry listing endpoint, then the `/info/<entries>` endpoint MUST include,\n for each field name `<fieldname>` in its `data.properties.<fieldname>` response value that can be used for sorting,\n the key `sortable` with value `true`.\n If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either\n leave out the `sortable` key or set it equal to `false` for the specific field name.\n The set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to\n its definition in the JSON API 1.0 specification.\n The field `sortable` is in addition to each property description and other OPTIONAL fields.\n An example is shown in the section Entry Listing Info Endpoints.\n\n page_limit (int): Sets a numerical limit on the number of entries returned.\n See [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\n The API implementation MUST return no more than the number specified.\n It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers\n (in which case an error code -- `403 Forbidden` -- MUST be returned).\n The default limit value is up to the API implementation to decide.\n\n **Example**: `http://example.com/optimade/v1/structures?page_limit=100`\n\n page_offset (int): RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\n\n **Example**: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.\n\n page_number (int): RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\n It is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\n\n **Example**: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.\n\n page_cursor (int): RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.\n\n page_above (str): RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\n\n **Example**: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by\n increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.\n\n page_below (str): RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\n\n include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents)\n by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\n All related resource objects MUST be returned as part of an array value for the top-level `included` field,\n see the section JSON Response Schema: Common Fields.\n\n The value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\n If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\n The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field\n `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\n Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level\n field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.\n\n api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`,\n where MAJOR is a major version and MINOR is a minor version of the API.\n For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\n\n \"\"\"\n\n # The reference server implementation only supports offset/number-based pagination\n unsupported_params: list[str] = [\n \"page_cursor\",\n \"page_below\",\n ]\n\n def __init__(\n self,\n *,\n filter: Annotated[\n str,\n Query(\n description=\"A filter string, in the format described in section API Filtering Format Specification of the specification.\",\n ),\n ] = \"\",\n response_format: Annotated[\n str,\n Query(\n description=\"The output format requested (see section Response Format).\\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\\nExample: `http://example.com/v1/structures?response_format=xml`\",\n ),\n ] = \"json\",\n email_address: Annotated[\n Optional[EmailStr],\n Query(\n description=\"An email address of the user making the request.\\nThe email SHOULD be that of a person and not an automatic system.\\nExample: `http://example.com/v1/structures?email_address=user@example.com`\",\n ),\n ] = None,\n response_fields: Annotated[\n str,\n Query(\n description=\"A comma-delimited set of fields to be provided in the output.\\nIf provided, these fields MUST be returned along with the REQUIRED fields.\\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`\",\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n sort: Annotated[\n str,\n Query(\n description='If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\\n\\nAn implementation MAY support multiple sort fields for a single query.\\nIf it does, it again MUST conform to the JSON API 1.0 specification.\\n\\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/<entries>` endpoint MUST include, for each field name `<fieldname>` in its `data.properties.<fieldname>` response value that can be used for sorting, the key `sortable` with value `true`.\\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\\nAn example is shown in the section Entry Listing Info Endpoints.',\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n page_limit: Annotated[\n int,\n Query(\n description=\"Sets a numerical limit on the number of entries returned.\\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\\nThe API implementation MUST return no more than the number specified.\\nIt MAY return fewer.\\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\\nThe default limit value is up to the API implementation to decide.\\nExample: `http://example.com/optimade/v1/structures?page_limit=100`\",\n ge=0,\n ),\n ] = CONFIG.page_limit,\n page_offset: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.\",\n ge=0,\n ),\n ] = 0,\n page_number: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.\",\n # ge=1, # This constraint is only 'RECOMMENDED' in the specification, so should not be included here or in the OpenAPI schema\n ),\n ] = None, # type: ignore[assignment]\n page_cursor: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.\",\n ge=0,\n ),\n ] = 0,\n page_above: Annotated[\n str,\n Query(\n description=\"RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.\",\n ),\n ] = None, # type: ignore[assignment]\n page_below: Annotated[\n str,\n Query(\n description=\"RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\",\n ),\n ] = None, # type: ignore[assignment]\n include: Annotated[\n str,\n Query(\n description='A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\\n\\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\\n\\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\\n\\nThe **default value** for `include` is `references`.\\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\\n\\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.',\n ),\n ] = \"references\",\n api_hint: Annotated[\n str,\n Query(\n description=\"If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\",\n pattern=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n ),\n ] = \"\",\n ):\n self.filter = filter\n self.response_format = response_format\n self.email_address = email_address\n self.response_fields = response_fields\n self.sort = sort\n self.page_limit = page_limit\n self.page_offset = page_offset\n self.page_number = page_number\n self.page_cursor = page_cursor\n self.page_above = page_above\n self.page_below = page_below\n self.include = include\n self.api_hint = api_hint\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.EntryListingQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.SingleEntryQueryParams","title":"SingleEntryQueryParams
","text":" Bases: BaseQueryParams
Common query params for single entry endpoints.
Attributes:
Name Type Descriptionresponse_format
str
The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification.
Example: http://example.com/v1/structures?response_format=xml
email_address
EmailStr
An email address of the user making the request. The email SHOULD be that of a person and not an automatic system.
Example: http://example.com/v1/structures?email_address=user@example.com
response_fields
str
A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present.
Example: http://example.com/v1/structures?response_fields=last_modified,nsites
include
str
A server MAY implement the JSON API concept of returning compound documents by utilizing the include
query parameter as specified by JSON API 1.0.
All related resource objects MUST be returned as part of an array value for the top-level included
field, see the section JSON Response Schema: Common Fields.
The value of include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a 400 Bad Request
response MUST be made.
The default value for include
is references
. This means references
entries MUST always be included under the top-level field included
as default, since a server assumes if include
is not specified by a client in the request, it is still specified as include=references
. Note, if a client explicitly specifies include
and leaves out references
, references
resource objects MUST NOT be included under the top-level field included
, as per the definition of included
, see section JSON Response Schema: Common Fields.
Note: A query with the parameter include
set to the empty string means no related resource objects are to be returned under the top-level field included
.
api_hint
str
If the client provides the parameter, the value SHOULD have the format vMAJOR
or vMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends api_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.
optimade/server/query_params.py
class SingleEntryQueryParams(BaseQueryParams):\n \"\"\"\n Common query params for single entry endpoints.\n\n Attributes:\n response_format (str): The output format requested (see section Response Format).\n Defaults to the format string 'json', which specifies the standard output format described in this specification.\n\n **Example**: `http://example.com/v1/structures?response_format=xml`\n\n email_address (EmailStr): An email address of the user making the request.\n The email SHOULD be that of a person and not an automatic system.\n\n **Example**: `http://example.com/v1/structures?email_address=user@example.com`\n\n response_fields (str): A comma-delimited set of fields to be provided in the output.\n If provided, these fields MUST be returned along with the REQUIRED fields.\n Other OPTIONAL fields MUST NOT be returned when this parameter is present.\n\n **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites`\n\n include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents)\n by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\n All related resource objects MUST be returned as part of an array value for the top-level `included` field,\n see the section JSON Response Schema: Common Fields.\n\n The value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\n If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\n The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field\n `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\n Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level\n field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.\n\n api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`,\n where MAJOR is a major version and MINOR is a minor version of the API.\n For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\n\n \"\"\"\n\n def __init__(\n self,\n *,\n response_format: Annotated[\n str,\n Query(\n description=\"The output format requested (see section Response Format).\\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\\nExample: `http://example.com/v1/structures?response_format=xml`\",\n ),\n ] = \"json\",\n email_address: Annotated[\n Optional[EmailStr],\n Query(\n description=\"An email address of the user making the request.\\nThe email SHOULD be that of a person and not an automatic system.\\nExample: `http://example.com/v1/structures?email_address=user@example.com`\",\n ),\n ] = None,\n response_fields: Annotated[\n str,\n Query(\n description=\"A comma-delimited set of fields to be provided in the output.\\nIf provided, these fields MUST be returned along with the REQUIRED fields.\\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`\",\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n include: Annotated[\n str,\n Query(\n description='A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\\n\\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\\n\\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\\n\\nThe **default value** for `include` is `references`.\\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\\n\\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.',\n ),\n ] = \"references\",\n api_hint: Annotated[\n str,\n Query(\n description=\"If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\",\n pattern=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n ),\n ] = \"\",\n ):\n self.response_format = response_format\n self.email_address = email_address\n self.response_fields = response_fields\n self.include = include\n self.api_hint = api_hint\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.SingleEntryQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/schemas/","title":"schemas","text":""},{"location":"api_reference/server/schemas/#optimade.server.schemas.ENTRY_INFO_SCHEMAS","title":"ENTRY_INFO_SCHEMAS: dict[str, type[EntryResource]] = {'structures': StructureResource, 'references': ReferenceResource}
module-attribute
","text":"This dictionary is used to define the /info/<entry_type>
endpoints.
retrieve_queryable_properties(schema, queryable_properties=None, entry_type=None)
","text":"Recursively loops through a pydantic model, returning a dictionary of all the OPTIMADE-queryable properties of that model.
Parameters:
Name Type Description Defaultschema
type[EntryResource]
The pydantic model.
requiredqueryable_properties
Optional[Iterable[str]]
The list of properties to find in the schema.
None
entry_type
Optional[str]
An optional entry type for the model. Will be used to lookup schemas for any config-defined fields.
None
Returns:
Type DescriptionQueryableProperties
A flat dictionary with properties as keys, containing the field
QueryableProperties
description, unit, sortability, support level, queryability
QueryableProperties
and type, where provided.
Source code inoptimade/server/schemas.py
def retrieve_queryable_properties(\n schema: type[EntryResource],\n queryable_properties: Optional[Iterable[str]] = None,\n entry_type: Optional[str] = None,\n) -> \"QueryableProperties\":\n \"\"\"Recursively loops through a pydantic model, returning a dictionary of all the\n OPTIMADE-queryable properties of that model.\n\n Parameters:\n schema: The pydantic model.\n queryable_properties: The list of properties to find in the schema.\n entry_type: An optional entry type for the model. Will be used to\n lookup schemas for any config-defined fields.\n\n Returns:\n A flat dictionary with properties as keys, containing the field\n description, unit, sortability, support level, queryability\n and type, where provided.\n\n \"\"\"\n properties: \"QueryableProperties\" = {}\n for name, value in schema.model_fields.items():\n # Proceed if the field (name) is given explicitly in the queryable_properties\n # list or if the queryable_properties list is empty (i.e., all properties are\n # requested)\n if not queryable_properties or name in queryable_properties:\n if name in properties:\n continue\n\n # If the field is another data model, \"unpack\" it by recursively calling\n # this function.\n # But first, we need to \"unpack\" the annotation, getting in behind any\n # Optional, Union, or Annotated types.\n annotation = _get_origin_type(value.annotation)\n\n if annotation not in (None, NoneType) and issubclass(annotation, BaseModel):\n sub_queryable_properties = list(annotation.model_fields) # type: ignore[attr-defined]\n properties.update(\n retrieve_queryable_properties(annotation, sub_queryable_properties)\n )\n\n properties[name] = {\"description\": value.description or \"\"}\n\n # Update schema with extension keys, provided they are not None\n for key in (\n \"x-optimade-unit\",\n \"x-optimade-queryable\",\n \"x-optimade-support\",\n ):\n if (\n value.json_schema_extra\n and value.json_schema_extra.get(key) is not None\n ):\n properties[name][\n key.replace(\"x-optimade-\", \"\") # type: ignore[index]\n ] = value.json_schema_extra[key]\n\n # All properties are sortable with the MongoDB backend.\n # While the result for sorting lists may not be as expected, they are still sorted.\n properties[name][\"sortable\"] = (\n value.json_schema_extra.get(\"x-optimade-sortable\", True)\n if value.json_schema_extra\n else True\n )\n\n # Try to get OpenAPI-specific \"format\" if possible, else get \"type\"; a mandatory OpenAPI key.\n json_schema = TypeAdapter(annotation).json_schema(mode=\"validation\")\n\n properties[name][\"type\"] = DataType.from_json_type(\n json_schema.get(\"format\", json_schema.get(\"type\"))\n )\n\n # If specified, check the config for any additional well-described provider fields\n if entry_type:\n from optimade.server.config import CONFIG\n\n described_provider_fields = [\n field\n for field in CONFIG.provider_fields.get(entry_type, {}) # type: ignore[call-overload]\n if isinstance(field, dict)\n ]\n for field in described_provider_fields:\n name = (\n f\"_{CONFIG.provider.prefix}_{field['name']}\"\n if not field[\"name\"].startswith(\"_\")\n else field[\"name\"]\n )\n properties[name] = {k: field[k] for k in field if k != \"name\"}\n properties[name][\"sortable\"] = field.get(\"sortable\", True)\n\n # Remove JSON fields mistaken as properties\n non_property_fields = [\"attributes\", \"relationships\"]\n for non_property_field in non_property_fields:\n if non_property_field in properties:\n del properties[non_property_field]\n\n return properties\n
"},{"location":"api_reference/server/warnings/","title":"warnings","text":"This submodule maintains backwards compatibility with the old optimade.server.warnings
module, which previously implemented the imported warnings directly.
FieldValueNotRecognized
","text":" Bases: OptimadeWarning
A field or value used in the request is not recognised by this implementation.
Source code inoptimade/warnings.py
class FieldValueNotRecognized(OptimadeWarning):\n \"\"\"A field or value used in the request is not recognised by this implementation.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.MissingExpectedField","title":"MissingExpectedField
","text":" Bases: LocalOptimadeWarning
A field was provided with a null value when a related field was provided with a value.
Source code inoptimade/warnings.py
class MissingExpectedField(LocalOptimadeWarning):\n \"\"\"A field was provided with a null value when a related field was provided\n with a value.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.OptimadeWarning","title":"OptimadeWarning
","text":" Bases: Warning
Base Warning for the optimade
package
optimade/warnings.py
class OptimadeWarning(Warning):\n \"\"\"Base Warning for the `optimade` package\"\"\"\n\n def __init__(\n self, detail: Optional[str] = None, title: Optional[str] = None, *args\n ) -> None:\n detail = detail if detail else self.__doc__\n super().__init__(detail, *args)\n self.detail = detail\n self.title = title if title else self.__class__.__name__\n\n def __repr__(self) -> str:\n attrs = {\"detail\": self.detail, \"title\": self.title}\n return \"<{:s}({:s})>\".format(\n self.__class__.__name__,\n \" \".join(\n [\n f\"{attr}={value!r}\"\n for attr, value in attrs.items()\n if value is not None\n ]\n ),\n )\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else \"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.QueryParamNotUsed","title":"QueryParamNotUsed
","text":" Bases: OptimadeWarning
A query parameter is not used in this request.
Source code inoptimade/warnings.py
class QueryParamNotUsed(OptimadeWarning):\n \"\"\"A query parameter is not used in this request.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.TimestampNotRFCCompliant","title":"TimestampNotRFCCompliant
","text":" Bases: OptimadeWarning
A timestamp has been used in a filter that contains microseconds and is thus not RFC 3339 compliant. This may cause undefined behaviour in the query results.
Source code inoptimade/warnings.py
class TimestampNotRFCCompliant(OptimadeWarning):\n \"\"\"A timestamp has been used in a filter that contains microseconds and is thus not\n RFC 3339 compliant. This may cause undefined behaviour in the query results.\n\n \"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.TooManyValues","title":"TooManyValues
","text":" Bases: OptimadeWarning
A field or query parameter has too many values to be handled by this implementation.
Source code inoptimade/warnings.py
class TooManyValues(OptimadeWarning):\n \"\"\"A field or query parameter has too many values to be handled by this implementation.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.UnknownProviderProperty","title":"UnknownProviderProperty
","text":" Bases: OptimadeWarning
A provider-specific property has been requested via response_fields
or as in a filter
that is not recognised by this implementation.
optimade/warnings.py
class UnknownProviderProperty(OptimadeWarning):\n \"\"\"A provider-specific property has been requested via `response_fields` or as in a `filter` that is not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.UnknownProviderQueryParameter","title":"UnknownProviderQueryParameter
","text":" Bases: OptimadeWarning
A provider-specific query parameter has been requested in the query with a prefix not recognised by this implementation.
Source code inoptimade/warnings.py
class UnknownProviderQueryParameter(OptimadeWarning):\n \"\"\"A provider-specific query parameter has been requested in the query with a prefix not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/","title":"elasticsearch","text":""},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection","title":"ElasticCollection
","text":" Bases: EntryCollection
optimade/server/entry_collections/elasticsearch.py
class ElasticCollection(EntryCollection):\n pagination_mechanism = PaginationMechanism(\"page_offset\")\n\n def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n client: Optional[\"Elasticsearch\"] = None,\n ):\n \"\"\"Initialize the ElasticCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n client: A preconfigured Elasticsearch client.\n\n \"\"\"\n super().__init__(\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n transformer=ElasticTransformer(mapper=resource_mapper),\n )\n\n self.client = client if client else CLIENT\n self.name = name\n\n # If we are creating a new collection from scratch, also create the index,\n # otherwise assume it has already been created externally\n if CONFIG.insert_test_data:\n self.create_optimade_index()\n\n def count(self, *args, **kwargs) -> int:\n raise NotImplementedError\n\n def create_optimade_index(self) -> None:\n \"\"\"Load or create an index that can handle aliased OPTIMADE fields and attach it\n to the current client.\n\n \"\"\"\n body = self.predefined_index.get(self.name)\n if body is None:\n body = self.create_elastic_index_from_mapper(\n self.resource_mapper, self.all_fields\n )\n\n properties = {}\n for field in list(body[\"mappings\"][\"properties\"].keys()):\n properties[self.resource_mapper.get_backend_field(field)] = body[\n \"mappings\"\n ][\"properties\"].pop(field)\n properties[\"id\"] = {\"type\": \"keyword\"}\n body[\"mappings\"][\"properties\"] = properties\n self.client.indices.create(index=self.name, ignore=400, **body)\n\n LOGGER.debug(f\"Created Elastic index for {self.name!r} with parameters {body}\")\n\n @property\n def predefined_index(self) -> dict[str, Any]:\n \"\"\"Loads and returns the default pre-defined index.\"\"\"\n with open(Path(__file__).parent.joinpath(\"elastic_indexes.json\")) as f:\n index = json.load(f)\n return index\n\n @staticmethod\n def create_elastic_index_from_mapper(\n resource_mapper: type[BaseResourceMapper], fields: Iterable[str]\n ) -> dict[str, Any]:\n \"\"\"Create a fallback elastic index based on a resource mapper.\n\n Arguments:\n resource_mapper: The resource mapper to create the index for.\n fields: The list of fields to use in the index.\n\n Returns:\n The parameters to pass to `client.indices.create(...)` (previously\n the 'body' parameters).\n\n \"\"\"\n properties = {\n resource_mapper.get_optimade_field(field): {\"type\": \"keyword\"}\n for field in fields\n }\n properties[\"id\"] = {\"type\": \"keyword\"}\n return {\"mappings\": {\"properties\": properties}}\n\n def __len__(self):\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return Search(using=self.client, index=self.name).execute().hits.total.value\n\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n def get_id(item):\n if self.name == \"links\":\n id_ = f\"{item['id']}-{item['type']}\"\n elif \"id\" in item:\n id_ = item[\"id\"]\n elif \"_id\" in item:\n # use the existing MongoDB ids in the test data\n id_ = str(item[\"_id\"])\n else:\n # ES will generate ids\n id_ = None\n item.pop(\"_id\", None)\n return id_\n\n bulk(\n self.client,\n (\n {\n \"_index\": self.name,\n \"_id\": get_id(item),\n \"_source\": item,\n }\n for item in data\n ),\n )\n\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry=False\n ) -> tuple[list[dict[str, Any]], int, bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n\n search = Search(using=self.client, index=self.name)\n\n if criteria.get(\"filter\", False):\n search = search.query(criteria[\"filter\"])\n\n page_offset = criteria.get(\"skip\", None)\n page_above = criteria.get(\"page_above\", None)\n\n limit = criteria.get(\"limit\", CONFIG.page_limit)\n\n all_aliased_fields = [\n self.resource_mapper.get_backend_field(field) for field in self.all_fields\n ]\n search = search.source(includes=all_aliased_fields)\n\n elastic_sort = [\n {field: {\"order\": \"desc\" if sort_dir == -1 else \"asc\"}}\n for field, sort_dir in criteria.get(\"sort\", {})\n ]\n if not elastic_sort:\n elastic_sort = [\n {self.resource_mapper.get_backend_field(\"id\"): {\"order\": \"asc\"}}\n ]\n\n search = search.sort(*elastic_sort)\n\n if page_offset:\n search = search[page_offset : page_offset + limit]\n\n elif page_above:\n search = search.extra(search_after=page_above, limit=limit)\n\n else:\n search = search[0:limit]\n page_offset = 0\n\n search = search.extra(track_total_hits=True)\n response = search.execute()\n\n results = [hit.to_dict() for hit in response.hits]\n\n more_data_available = False\n if not single_entry:\n data_returned = response.hits.total.value\n if page_above is not None:\n more_data_available = len(results) == limit and data_returned != limit\n else:\n more_data_available = page_offset + limit < data_returned\n else:\n # SingleEntryQueryParams, e.g., /structures/{entry_id}\n data_returned = len(results)\n\n return results, data_returned, more_data_available\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.predefined_index","title":"predefined_index: dict[str, Any]
property
","text":"Loads and returns the default pre-defined index.
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.__init__","title":"__init__(name, resource_cls, resource_mapper, client=None)
","text":"Initialize the ElasticCollection for the given parameters.
Parameters:
Name Type Description Defaultname
str
The name of the collection.
requiredresource_cls
type[EntryResource]
The type of entry resource that is stored by the collection.
requiredresource_mapper
type[BaseResourceMapper]
A resource mapper object that handles aliases and format changes between deserialization and response.
requiredclient
Optional[Elasticsearch]
A preconfigured Elasticsearch client.
None
Source code in optimade/server/entry_collections/elasticsearch.py
def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n client: Optional[\"Elasticsearch\"] = None,\n):\n \"\"\"Initialize the ElasticCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n client: A preconfigured Elasticsearch client.\n\n \"\"\"\n super().__init__(\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n transformer=ElasticTransformer(mapper=resource_mapper),\n )\n\n self.client = client if client else CLIENT\n self.name = name\n\n # If we are creating a new collection from scratch, also create the index,\n # otherwise assume it has already been created externally\n if CONFIG.insert_test_data:\n self.create_optimade_index()\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.__len__","title":"__len__()
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/elasticsearch.py
def __len__(self):\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return Search(using=self.client, index=self.name).execute().hits.total.value\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.create_elastic_index_from_mapper","title":"create_elastic_index_from_mapper(resource_mapper, fields)
staticmethod
","text":"Create a fallback elastic index based on a resource mapper.
Parameters:
Name Type Description Defaultresource_mapper
type[BaseResourceMapper]
The resource mapper to create the index for.
requiredfields
Iterable[str]
The list of fields to use in the index.
requiredReturns:
Type Descriptiondict[str, Any]
The parameters to pass to client.indices.create(...)
(previously the 'body' parameters).
optimade/server/entry_collections/elasticsearch.py
@staticmethod\ndef create_elastic_index_from_mapper(\n resource_mapper: type[BaseResourceMapper], fields: Iterable[str]\n) -> dict[str, Any]:\n \"\"\"Create a fallback elastic index based on a resource mapper.\n\n Arguments:\n resource_mapper: The resource mapper to create the index for.\n fields: The list of fields to use in the index.\n\n Returns:\n The parameters to pass to `client.indices.create(...)` (previously\n the 'body' parameters).\n\n \"\"\"\n properties = {\n resource_mapper.get_optimade_field(field): {\"type\": \"keyword\"}\n for field in fields\n }\n properties[\"id\"] = {\"type\": \"keyword\"}\n return {\"mappings\": {\"properties\": properties}}\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.create_optimade_index","title":"create_optimade_index()
","text":"Load or create an index that can handle aliased OPTIMADE fields and attach it to the current client.
Source code inoptimade/server/entry_collections/elasticsearch.py
def create_optimade_index(self) -> None:\n \"\"\"Load or create an index that can handle aliased OPTIMADE fields and attach it\n to the current client.\n\n \"\"\"\n body = self.predefined_index.get(self.name)\n if body is None:\n body = self.create_elastic_index_from_mapper(\n self.resource_mapper, self.all_fields\n )\n\n properties = {}\n for field in list(body[\"mappings\"][\"properties\"].keys()):\n properties[self.resource_mapper.get_backend_field(field)] = body[\n \"mappings\"\n ][\"properties\"].pop(field)\n properties[\"id\"] = {\"type\": \"keyword\"}\n body[\"mappings\"][\"properties\"] = properties\n self.client.indices.create(index=self.name, ignore=400, **body)\n\n LOGGER.debug(f\"Created Elastic index for {self.name!r} with parameters {body}\")\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend.
NoteCurrently this method returns the pymongo interpretation of the parameters, which will need modification for modified for other backends.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.insert","title":"insert(data)
","text":"Add the given entries to the underlying database.
WarningNo validation is performed on the incoming data.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/elasticsearch.py
def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n def get_id(item):\n if self.name == \"links\":\n id_ = f\"{item['id']}-{item['type']}\"\n elif \"id\" in item:\n id_ = item[\"id\"]\n elif \"_id\" in item:\n # use the existing MongoDB ids in the test data\n id_ = str(item[\"_id\"])\n else:\n # ES will generate ids\n id_ = None\n item.pop(\"_id\", None)\n return id_\n\n bulk(\n self.client,\n (\n {\n \"_index\": self.name,\n \"_id\": get_id(item),\n \"_source\": item,\n }\n for item in data\n ),\n )\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/entry_collections/entry_collections/","title":"entry_collections","text":""},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection","title":"EntryCollection
","text":" Bases: ABC
Backend-agnostic base class for querying collections of EntryResource
s.
optimade/server/entry_collections/entry_collections.py
class EntryCollection(ABC):\n \"\"\"Backend-agnostic base class for querying collections of\n [`EntryResource`][optimade.models.entries.EntryResource]s.\"\"\"\n\n pagination_mechanism = PaginationMechanism(\"page_offset\")\n \"\"\"The default pagination mechansim to use with a given collection,\n if the user does not provide any pagination query parameters.\n \"\"\"\n\n def __init__(\n self,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n transformer: Transformer,\n ):\n \"\"\"Initialize the collection for the given parameters.\n\n Parameters:\n resource_cls (EntryResource): The `EntryResource` model\n that is stored by the collection.\n resource_mapper (BaseResourceMapper): A resource mapper\n object that handles aliases and format changes between\n deserialization and response.\n transformer (Transformer): The Lark `Transformer` used to\n interpret the filter.\n\n \"\"\"\n self.parser = LarkParser()\n self.resource_cls = resource_cls\n self.resource_mapper = resource_mapper\n self.transformer = transformer\n\n self.provider_prefix = CONFIG.provider.prefix\n self.provider_fields = [\n field if isinstance(field, str) else field[\"name\"]\n for field in CONFIG.provider_fields.get(resource_mapper.ENDPOINT, [])\n ]\n\n self._all_fields: set[str] = set()\n\n @abstractmethod\n def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n\n @abstractmethod\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n @abstractmethod\n def count(self, **kwargs: Any) -> Optional[int]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments.\n\n \"\"\"\n\n def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n ]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n\n @abstractmethod\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry: bool = False\n ) -> tuple[list[dict[str, Any]], Optional[int], bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n\n @property\n def all_fields(self) -> set[str]:\n \"\"\"Get the set of all fields handled in this collection,\n from attribute fields in the schema, provider fields and top-level OPTIMADE fields.\n\n The set of all fields are lazily created and then cached.\n This means the set is created the first time the property is requested and then cached.\n\n Returns:\n All fields handled in this collection.\n\n \"\"\"\n if not self._all_fields:\n # All OPTIMADE fields\n self._all_fields = (\n self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS.copy()\n )\n self._all_fields |= self.get_attribute_fields()\n # All provider-specific fields\n self._all_fields |= {\n f\"_{self.provider_prefix}_{field_name}\"\n if not field_name.startswith(\"_\")\n else field_name\n for field_name in self.provider_fields\n }\n\n return self._all_fields\n\n def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n\n def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n\n def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n\n def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n ) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.pagination_mechanism","title":"pagination_mechanism = PaginationMechanism('page_offset')
class-attribute
instance-attribute
","text":"The default pagination mechansim to use with a given collection, if the user does not provide any pagination query parameters.
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.__init__","title":"__init__(resource_cls, resource_mapper, transformer)
","text":"Initialize the collection for the given parameters.
Parameters:
Name Type Description Defaultresource_cls
EntryResource
The EntryResource
model that is stored by the collection.
resource_mapper
BaseResourceMapper
A resource mapper object that handles aliases and format changes between deserialization and response.
requiredtransformer
Transformer
The Lark Transformer
used to interpret the filter.
optimade/server/entry_collections/entry_collections.py
def __init__(\n self,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n transformer: Transformer,\n):\n \"\"\"Initialize the collection for the given parameters.\n\n Parameters:\n resource_cls (EntryResource): The `EntryResource` model\n that is stored by the collection.\n resource_mapper (BaseResourceMapper): A resource mapper\n object that handles aliases and format changes between\n deserialization and response.\n transformer (Transformer): The Lark `Transformer` used to\n interpret the filter.\n\n \"\"\"\n self.parser = LarkParser()\n self.resource_cls = resource_cls\n self.resource_mapper = resource_mapper\n self.transformer = transformer\n\n self.provider_prefix = CONFIG.provider.prefix\n self.provider_fields = [\n field if isinstance(field, str) else field[\"name\"]\n for field in CONFIG.provider_fields.get(resource_mapper.ENDPOINT, [])\n ]\n\n self._all_fields: set[str] = set()\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.__len__","title":"__len__()
abstractmethod
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.count","title":"count(**kwargs)
abstractmethod
","text":"Returns the number of entries matching the query specified by the keyword arguments.
Parameters:
Name Type Description Default**kwargs
Any
Query parameters as keyword arguments.
{}
Source code in optimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef count(self, **kwargs: Any) -> Optional[int]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend.
NoteCurrently this method returns the pymongo interpretation of the parameters, which will need modification for modified for other backends.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.insert","title":"insert(data)
abstractmethod
","text":"Add the given entries to the underlying database.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.PaginationMechanism","title":"PaginationMechanism
","text":" Bases: Enum
The supported pagination mechanisms.
Source code inoptimade/server/entry_collections/entry_collections.py
class PaginationMechanism(enum.Enum):\n \"\"\"The supported pagination mechanisms.\"\"\"\n\n OFFSET = \"page_offset\"\n NUMBER = \"page_number\"\n CURSOR = \"page_cursor\"\n ABOVE = \"page_above\"\n BELOW = \"page_below\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.create_collection","title":"create_collection(name, resource_cls, resource_mapper)
","text":"Create an EntryCollection
of the configured type, depending on the value of CONFIG.database_backend
.
Parameters:
Name Type Description Defaultname
str
The collection name.
requiredresource_cls
type[EntryResource]
The type of entry resource to be stored within the collection.
requiredresource_mapper
type[BaseResourceMapper]
The associated resource mapper for that entry resource type.
requiredReturns:
Type DescriptionEntryCollection
The created EntryCollection
.
optimade/server/entry_collections/entry_collections.py
def create_collection(\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n) -> \"EntryCollection\":\n \"\"\"Create an `EntryCollection` of the configured type, depending on the value of\n `CONFIG.database_backend`.\n\n Arguments:\n name: The collection name.\n resource_cls: The type of entry resource to be stored within the collection.\n resource_mapper: The associated resource mapper for that entry resource type.\n\n Returns:\n The created `EntryCollection`.\n\n \"\"\"\n if CONFIG.database_backend in (\n SupportedBackend.MONGODB,\n SupportedBackend.MONGOMOCK,\n ):\n from optimade.server.entry_collections.mongo import MongoCollection\n\n return MongoCollection(\n name=name,\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n )\n\n if CONFIG.database_backend is SupportedBackend.ELASTIC:\n from optimade.server.entry_collections.elasticsearch import ElasticCollection\n\n return ElasticCollection(\n name=name,\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n )\n\n raise NotImplementedError(\n f\"The database backend {CONFIG.database_backend!r} is not implemented\"\n )\n
"},{"location":"api_reference/server/entry_collections/mongo/","title":"mongo","text":""},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection","title":"MongoCollection
","text":" Bases: EntryCollection
Class for querying MongoDB collections (implemented by either pymongo or mongomock) containing serialized EntryResource
s objects.
optimade/server/entry_collections/mongo.py
class MongoCollection(EntryCollection):\n \"\"\"Class for querying MongoDB collections (implemented by either pymongo or mongomock)\n containing serialized [`EntryResource`][optimade.models.entries.EntryResource]s objects.\n\n \"\"\"\n\n def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n database: str = CONFIG.mongo_database,\n ):\n \"\"\"Initialize the MongoCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n database: The name of the underlying MongoDB database to connect to.\n\n \"\"\"\n super().__init__(\n resource_cls,\n resource_mapper,\n MongoTransformer(mapper=resource_mapper),\n )\n\n self.collection = CLIENT[database][name]\n\n # check aliases do not clash with mongo operators\n self._check_aliases(self.resource_mapper.all_aliases())\n self._check_aliases(self.resource_mapper.all_length_aliases())\n\n def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return self.collection.estimated_document_count()\n\n def count(self, **kwargs: Any) -> Union[int, None]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments, or `None` if the count timed out.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments. The keys\n 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed\n to the `pymongo.collection.Collection.count_documents` method.\n\n \"\"\"\n for k in list(kwargs.keys()):\n if k not in (\"filter\", \"skip\", \"limit\", \"hint\", \"maxTimeMS\"):\n del kwargs[k]\n if \"filter\" not in kwargs:\n return self.collection.estimated_document_count()\n else:\n if \"maxTimeMS\" not in kwargs:\n kwargs[\"maxTimeMS\"] = 1000 * CONFIG.mongo_count_timeout\n try:\n return self.collection.count_documents(**kwargs)\n except ExecutionTimeout:\n return None\n\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n self.collection.insert_many(data)\n\n def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by MongoDB.\n\n This Mongo-specific method calls the base `EntryCollection.handle_query_params` method\n and adds additional handling of the MongoDB ObjectID type.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n criteria = super().handle_query_params(params)\n # Handle MongoDB ObjectIDs:\n # - If they were not requested, then explicitly remove them\n # - If they were requested, then cast them to strings in the response\n if \"_id\" not in criteria.get(\"projection\", {}):\n criteria[\"projection\"][\"_id\"] = False\n\n if \"page_above\" in criteria:\n raise NotImplementedError(\n \"`page_above` is not implemented for this backend.\"\n )\n\n if criteria.get(\"projection\", {}).get(\"_id\"):\n criteria[\"projection\"][\"_id\"] = {\"$toString\": \"$_id\"}\n\n return criteria\n\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry: bool = False\n ) -> tuple[list[dict[str, Any]], Optional[int], bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n results = list(self.collection.find(**criteria))\n\n if CONFIG.database_backend == SupportedBackend.MONGOMOCK and criteria.get(\n \"projection\", {}\n ).get(\"_id\"):\n # mongomock does not support `$toString`` in projection, so we have to do it manually\n for ind, doc in enumerate(results):\n results[ind][\"_id\"] = str(doc[\"_id\"])\n\n nresults_now = len(results)\n if not single_entry:\n criteria_nolimit = criteria.copy()\n criteria_nolimit.pop(\"limit\", None)\n skip = criteria_nolimit.pop(\"skip\", 0)\n data_returned = self.count(**criteria_nolimit)\n # Only correct most of the time: if the total number of remaining results is exactly the page limit\n # then this will incorrectly say there is more_data_available\n if data_returned is None:\n more_data_available = nresults_now == criteria.get(\"limit\", 0)\n else:\n more_data_available = nresults_now + skip < data_returned\n else:\n # SingleEntryQueryParams, e.g., /structures/{entry_id}\n data_returned = nresults_now\n more_data_available = False\n\n return results, data_returned, more_data_available\n\n def _check_aliases(self, aliases):\n \"\"\"Check that aliases do not clash with mongo keywords.\"\"\"\n if any(\n alias[0].startswith(\"$\") or alias[1].startswith(\"$\") for alias in aliases\n ):\n raise RuntimeError(f\"Cannot define an alias starting with a '$': {aliases}\")\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.pagination_mechanism","title":"pagination_mechanism = PaginationMechanism('page_offset')
class-attribute
instance-attribute
","text":"The default pagination mechansim to use with a given collection, if the user does not provide any pagination query parameters.
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.__init__","title":"__init__(name, resource_cls, resource_mapper, database=CONFIG.mongo_database)
","text":"Initialize the MongoCollection for the given parameters.
Parameters:
Name Type Description Defaultname
str
The name of the collection.
requiredresource_cls
type[EntryResource]
The type of entry resource that is stored by the collection.
requiredresource_mapper
type[BaseResourceMapper]
A resource mapper object that handles aliases and format changes between deserialization and response.
requireddatabase
str
The name of the underlying MongoDB database to connect to.
mongo_database
Source code in optimade/server/entry_collections/mongo.py
def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n database: str = CONFIG.mongo_database,\n):\n \"\"\"Initialize the MongoCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n database: The name of the underlying MongoDB database to connect to.\n\n \"\"\"\n super().__init__(\n resource_cls,\n resource_mapper,\n MongoTransformer(mapper=resource_mapper),\n )\n\n self.collection = CLIENT[database][name]\n\n # check aliases do not clash with mongo operators\n self._check_aliases(self.resource_mapper.all_aliases())\n self._check_aliases(self.resource_mapper.all_length_aliases())\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.__len__","title":"__len__()
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/mongo.py
def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return self.collection.estimated_document_count()\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.count","title":"count(**kwargs)
","text":"Returns the number of entries matching the query specified by the keyword arguments, or None
if the count timed out.
Parameters:
Name Type Description Default**kwargs
Any
Query parameters as keyword arguments. The keys 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed to the pymongo.collection.Collection.count_documents
method.
{}
Source code in optimade/server/entry_collections/mongo.py
def count(self, **kwargs: Any) -> Union[int, None]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments, or `None` if the count timed out.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments. The keys\n 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed\n to the `pymongo.collection.Collection.count_documents` method.\n\n \"\"\"\n for k in list(kwargs.keys()):\n if k not in (\"filter\", \"skip\", \"limit\", \"hint\", \"maxTimeMS\"):\n del kwargs[k]\n if \"filter\" not in kwargs:\n return self.collection.estimated_document_count()\n else:\n if \"maxTimeMS\" not in kwargs:\n kwargs[\"maxTimeMS\"] = 1000 * CONFIG.mongo_count_timeout\n try:\n return self.collection.count_documents(**kwargs)\n except ExecutionTimeout:\n return None\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB.
This Mongo-specific method calls the base EntryCollection.handle_query_params
method and adds additional handling of the MongoDB ObjectID type.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/mongo.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by MongoDB.\n\n This Mongo-specific method calls the base `EntryCollection.handle_query_params` method\n and adds additional handling of the MongoDB ObjectID type.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n criteria = super().handle_query_params(params)\n # Handle MongoDB ObjectIDs:\n # - If they were not requested, then explicitly remove them\n # - If they were requested, then cast them to strings in the response\n if \"_id\" not in criteria.get(\"projection\", {}):\n criteria[\"projection\"][\"_id\"] = False\n\n if \"page_above\" in criteria:\n raise NotImplementedError(\n \"`page_above` is not implemented for this backend.\"\n )\n\n if criteria.get(\"projection\", {}).get(\"_id\"):\n criteria[\"projection\"][\"_id\"] = {\"$toString\": \"$_id\"}\n\n return criteria\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.insert","title":"insert(data)
","text":"Add the given entries to the underlying database.
WarningNo validation is performed on the incoming data.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/mongo.py
def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n self.collection.insert_many(data)\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/mappers/entries/","title":"entries","text":""},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper","title":"BaseResourceMapper
","text":"Generic Resource Mapper that defines and performs the mapping between objects in the database and the resource objects defined by the specification.
Attributes:
Name Type DescriptionALIASES
tuple[tuple[str, str], ...]
a tuple of aliases between OPTIMADE field names and the field names in the database , e.g. ((\"elements\", \"custom_elements_field\"))
.
LENGTH_ALIASES
tuple[tuple[str, str], ...]
a tuple of aliases between a field name and another field that defines its length, to be used when querying, e.g. ((\"elements\", \"nelements\"))
. e.g. ((\"elements\", \"custom_elements_field\"))
.
ENTRY_RESOURCE_CLASS
type[EntryResource]
The entry type that this mapper corresponds to.
PROVIDER_FIELDS
tuple[str, ...]
a tuple of extra field names that this mapper should support when querying with the database prefix.
TOP_LEVEL_NON_ATTRIBUTES_FIELDS
set[str]
the set of top-level field names common to all endpoints.
SUPPORTED_PREFIXES
set[str]
The set of prefixes registered by this mapper.
ALL_ATTRIBUTES
set[str]
The set of attributes defined across the entry resource class and the server configuration.
ENTRY_RESOURCE_ATTRIBUTES
dict[str, Any]
A dictionary of attributes and their definitions defined by the schema of the entry resource class.
ENDPOINT
str
The expected endpoint name for this resource, as defined by the type
in the schema of the entry resource class.
optimade/server/mappers/entries.py
class BaseResourceMapper:\n \"\"\"Generic Resource Mapper that defines and performs the mapping\n between objects in the database and the resource objects defined by\n the specification.\n\n Attributes:\n ALIASES: a tuple of aliases between\n OPTIMADE field names and the field names in the database ,\n e.g. `((\"elements\", \"custom_elements_field\"))`.\n LENGTH_ALIASES: a tuple of aliases between\n a field name and another field that defines its length, to be used\n when querying, e.g. `((\"elements\", \"nelements\"))`.\n e.g. `((\"elements\", \"custom_elements_field\"))`.\n ENTRY_RESOURCE_CLASS: The entry type that this mapper corresponds to.\n PROVIDER_FIELDS: a tuple of extra field names that this\n mapper should support when querying with the database prefix.\n TOP_LEVEL_NON_ATTRIBUTES_FIELDS: the set of top-level\n field names common to all endpoints.\n SUPPORTED_PREFIXES: The set of prefixes registered by this mapper.\n ALL_ATTRIBUTES: The set of attributes defined across the entry\n resource class and the server configuration.\n ENTRY_RESOURCE_ATTRIBUTES: A dictionary of attributes and their definitions\n defined by the schema of the entry resource class.\n ENDPOINT: The expected endpoint name for this resource, as defined by\n the `type` in the schema of the entry resource class.\n\n \"\"\"\n\n try:\n from optimade.server.data import providers as PROVIDERS # type: ignore\n except (ImportError, ModuleNotFoundError):\n PROVIDERS = {}\n\n KNOWN_PROVIDER_PREFIXES: set[str] = {\n prov[\"id\"] for prov in PROVIDERS.get(\"data\", [])\n }\n ALIASES: tuple[tuple[str, str], ...] = ()\n LENGTH_ALIASES: tuple[tuple[str, str], ...] = ()\n PROVIDER_FIELDS: tuple[str, ...] = ()\n ENTRY_RESOURCE_CLASS: type[EntryResource] = EntryResource\n RELATIONSHIP_ENTRY_TYPES: set[str] = {\"references\", \"structures\"}\n TOP_LEVEL_NON_ATTRIBUTES_FIELDS: set[str] = {\"id\", \"type\", \"relationships\", \"links\"}\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n\n @classproperty\n @lru_cache(maxsize=1)\n def SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n\n @classproperty\n def ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n\n @classproperty\n def ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n\n @classproperty\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n\n @classmethod\n @lru_cache(maxsize=128)\n def length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n\n @classmethod\n @lru_cache(maxsize=128)\n def get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n\n @classmethod\n @lru_cache(maxsize=128)\n def alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n\n @classmethod\n @lru_cache(maxsize=128)\n def get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n\n @classmethod\n @lru_cache(maxsize=128)\n def alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n\n @classmethod\n def map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE.\n\n Starting from a MongoDB document `doc`, map the DB fields to the corresponding OPTIMADE fields.\n Then, the fields are all added to the top-level field \"attributes\",\n with the exception of other top-level fields, defined in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS`.\n All fields not in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS` + \"attributes\" will be removed.\n Finally, the `type` is given the value of the specified `cls.ENDPOINT`.\n\n Parameters:\n doc: A resource object in MongoDB format.\n\n Returns:\n A resource object in OPTIMADE format.\n\n \"\"\"\n mapping = ((real, alias) for alias, real in cls.all_aliases())\n newdoc = {}\n reals = {real for _, real in cls.all_aliases()}\n for key in doc:\n if key not in reals:\n newdoc[key] = doc[key]\n for real, alias in mapping:\n if real in doc:\n newdoc[alias] = doc[real]\n\n if \"attributes\" in newdoc:\n raise Exception(\"Will overwrite doc field!\")\n attributes = newdoc.copy()\n\n for field in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n value = attributes.pop(field, None)\n if value is not None:\n newdoc[field] = value\n for field in list(newdoc.keys()):\n if field not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n del newdoc[field]\n\n newdoc[\"type\"] = cls.ENDPOINT\n newdoc[\"attributes\"] = attributes\n\n return newdoc\n\n @classmethod\n def deserialize(\n cls, results: Union[dict, Iterable[dict]]\n ) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ALL_ATTRIBUTES","title":"ALL_ATTRIBUTES()
","text":"Returns all attributes served by this entry.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ENDPOINT","title":"ENDPOINT()
cached
","text":"Returns the expected endpoint for this mapper, corresponding to the type
property of the resource class.
optimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ENTRY_RESOURCE_ATTRIBUTES","title":"ENTRY_RESOURCE_ATTRIBUTES()
","text":"Returns the dictionary of attributes defined by the underlying entry resource class.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.SUPPORTED_PREFIXES","title":"SUPPORTED_PREFIXES()
cached
","text":"A set of prefixes handled by this entry type.
Note
This implementation only includes the provider prefix, but in the future this property may be extended to include other namespaces (for serving fields from, e.g., other providers or domain-specific terms).
Source code inoptimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=1)\ndef SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.alias_for","title":"alias_for(field)
cached
classmethod
","text":"Return aliased field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_backend_field()
.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type Descriptionstr
Aliased field as found in all_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.alias_of","title":"alias_of(field)
cached
classmethod
","text":"Return de-aliased field name, if it exists, otherwise return the input field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_optimade_field()
.
Parameters:
Name Type Description Defaultfield
str
Field name to be de-aliased.
requiredReturns:
Type Descriptionstr
De-aliased field name, falling back to returning field
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.all_aliases","title":"all_aliases()
cached
classmethod
","text":"Returns all of the associated aliases for this entry type, including those defined by the server config. The first member of each tuple is the OPTIMADE-compliant field name, the second is the backend-specific field name.
Returns:
Type DescriptionIterable[tuple[str, str]]
A tuple of alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases","title":"all_length_aliases()
cached
classmethod
","text":"Returns all of the associated length aliases for this class, including those defined by the server config.
Returns:
Type Descriptiontuple[tuple[str, str], ...]
A tuple of length alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.deserialize","title":"deserialize(results)
classmethod
","text":"Converts the raw database entries for this class into serialized models, mapping the data along the way.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef deserialize(\n cls, results: Union[dict, Iterable[dict]]\n) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_backend_field","title":"get_backend_field(optimade_field)
cached
classmethod
","text":"Return the field name configured for the particular underlying database for the passed OPTIMADE field name, that would be used in an API filter.
Aliases are read from all_aliases()
.
If a dot-separated OPTIMADE field is provided, e.g., species.mass
, only the first part will be mapped. This means for an (OPTIMADE, DB) alias of (species
, kinds
), get_backend_fields(\"species.mass\")
will return kinds.mass
.
Parameters:
Name Type Description Defaultoptimade_field
str
The OPTIMADE field to attempt to map to the backend-specific field.
requiredExamples:
>>> get_backend_field(\"chemical_formula_anonymous\")\n'formula_anon'\n>>> get_backend_field(\"formula_anon\")\n'formula_anon'\n>>> get_backend_field(\"_exmpl_custom_provider_field\")\n'custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in the query to the backend.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field","title":"get_optimade_field(backend_field)
cached
classmethod
","text":"Return the corresponding OPTIMADE field name for the underlying database field, ready to be used to construct the OPTIMADE-compliant JSON response.
Aliases are read from all_aliases()
.
Parameters:
Name Type Description Defaultbackend_field
str
The backend field to attempt to map to an OPTIMADE field.
requiredExamples:
>>> get_optimade_field(\"chemical_formula_anonymous\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"formula_anon\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"custom_provider_field\")\n'_exmpl_custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in an OPTIMADE-compliant response.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_required_fields","title":"get_required_fields()
cached
classmethod
","text":"Get REQUIRED response fields.
Returns:
Type Descriptionset
REQUIRED response fields.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.length_alias_for","title":"length_alias_for(field)
cached
classmethod
","text":"Returns the length alias for the particular field, or None
if no such alias is found.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type DescriptionOptional[str]
Aliased field as found in all_length_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.map_back","title":"map_back(doc)
classmethod
","text":"Map properties from MongoDB to OPTIMADE.
Starting from a MongoDB document doc
, map the DB fields to the corresponding OPTIMADE fields. Then, the fields are all added to the top-level field \"attributes\", with the exception of other top-level fields, defined in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS
. All fields not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS
+ \"attributes\" will be removed. Finally, the type
is given the value of the specified cls.ENDPOINT
.
Parameters:
Name Type Description Defaultdoc
dict
A resource object in MongoDB format.
requiredReturns:
Type Descriptiondict
A resource object in OPTIMADE format.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE.\n\n Starting from a MongoDB document `doc`, map the DB fields to the corresponding OPTIMADE fields.\n Then, the fields are all added to the top-level field \"attributes\",\n with the exception of other top-level fields, defined in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS`.\n All fields not in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS` + \"attributes\" will be removed.\n Finally, the `type` is given the value of the specified `cls.ENDPOINT`.\n\n Parameters:\n doc: A resource object in MongoDB format.\n\n Returns:\n A resource object in OPTIMADE format.\n\n \"\"\"\n mapping = ((real, alias) for alias, real in cls.all_aliases())\n newdoc = {}\n reals = {real for _, real in cls.all_aliases()}\n for key in doc:\n if key not in reals:\n newdoc[key] = doc[key]\n for real, alias in mapping:\n if real in doc:\n newdoc[alias] = doc[real]\n\n if \"attributes\" in newdoc:\n raise Exception(\"Will overwrite doc field!\")\n attributes = newdoc.copy()\n\n for field in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n value = attributes.pop(field, None)\n if value is not None:\n newdoc[field] = value\n for field in list(newdoc.keys()):\n if field not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n del newdoc[field]\n\n newdoc[\"type\"] = cls.ENDPOINT\n newdoc[\"attributes\"] = attributes\n\n return newdoc\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.classproperty","title":"classproperty
","text":" Bases: property
A simple extension of the property decorator that binds to types rather than instances.
Modelled on this StackOverflow answer with some tweaks to allow mkdocstrings to do its thing.
Source code inoptimade/server/mappers/entries.py
class classproperty(property):\n \"\"\"A simple extension of the property decorator that binds to types\n rather than instances.\n\n Modelled on this [StackOverflow answer](https://stackoverflow.com/a/5192374)\n with some tweaks to allow mkdocstrings to do its thing.\n\n \"\"\"\n\n def __init__(self, func):\n self.__name__ = func.__name__\n self.__module__ = func.__module__\n self.__doc__ = func.__doc__\n self.__wrapped__ = func\n\n def __get__(self, _: Any, owner: Optional[type] = None) -> Any:\n return self.__wrapped__(owner)\n
"},{"location":"api_reference/server/mappers/links/","title":"links","text":""},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper","title":"LinksMapper
","text":" Bases: BaseResourceMapper
optimade/server/mappers/links.py
class LinksMapper(BaseResourceMapper):\n ENTRY_RESOURCE_CLASS = LinksResource\n\n @classmethod\n def map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE\n\n :param doc: A resource object in MongoDB format\n :type doc: dict\n\n :return: A resource object in OPTIMADE format\n :rtype: dict\n \"\"\"\n type_ = doc[\"type\"]\n newdoc = super().map_back(doc)\n newdoc[\"type\"] = type_\n return newdoc\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ALL_ATTRIBUTES","title":"ALL_ATTRIBUTES()
","text":"Returns all attributes served by this entry.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ENDPOINT","title":"ENDPOINT()
cached
","text":"Returns the expected endpoint for this mapper, corresponding to the type
property of the resource class.
optimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ENTRY_RESOURCE_ATTRIBUTES","title":"ENTRY_RESOURCE_ATTRIBUTES()
","text":"Returns the dictionary of attributes defined by the underlying entry resource class.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.SUPPORTED_PREFIXES","title":"SUPPORTED_PREFIXES()
cached
","text":"A set of prefixes handled by this entry type.
Note
This implementation only includes the provider prefix, but in the future this property may be extended to include other namespaces (for serving fields from, e.g., other providers or domain-specific terms).
Source code inoptimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=1)\ndef SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.alias_for","title":"alias_for(field)
cached
classmethod
","text":"Return aliased field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_backend_field()
.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type Descriptionstr
Aliased field as found in all_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.alias_of","title":"alias_of(field)
cached
classmethod
","text":"Return de-aliased field name, if it exists, otherwise return the input field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_optimade_field()
.
Parameters:
Name Type Description Defaultfield
str
Field name to be de-aliased.
requiredReturns:
Type Descriptionstr
De-aliased field name, falling back to returning field
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.all_aliases","title":"all_aliases()
cached
classmethod
","text":"Returns all of the associated aliases for this entry type, including those defined by the server config. The first member of each tuple is the OPTIMADE-compliant field name, the second is the backend-specific field name.
Returns:
Type DescriptionIterable[tuple[str, str]]
A tuple of alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.all_length_aliases","title":"all_length_aliases()
cached
classmethod
","text":"Returns all of the associated length aliases for this class, including those defined by the server config.
Returns:
Type Descriptiontuple[tuple[str, str], ...]
A tuple of length alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.deserialize","title":"deserialize(results)
classmethod
","text":"Converts the raw database entries for this class into serialized models, mapping the data along the way.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef deserialize(\n cls, results: Union[dict, Iterable[dict]]\n) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_backend_field","title":"get_backend_field(optimade_field)
cached
classmethod
","text":"Return the field name configured for the particular underlying database for the passed OPTIMADE field name, that would be used in an API filter.
Aliases are read from all_aliases()
.
If a dot-separated OPTIMADE field is provided, e.g., species.mass
, only the first part will be mapped. This means for an (OPTIMADE, DB) alias of (species
, kinds
), get_backend_fields(\"species.mass\")
will return kinds.mass
.
Parameters:
Name Type Description Defaultoptimade_field
str
The OPTIMADE field to attempt to map to the backend-specific field.
requiredExamples:
>>> get_backend_field(\"chemical_formula_anonymous\")\n'formula_anon'\n>>> get_backend_field(\"formula_anon\")\n'formula_anon'\n>>> get_backend_field(\"_exmpl_custom_provider_field\")\n'custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in the query to the backend.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_optimade_field","title":"get_optimade_field(backend_field)
cached
classmethod
","text":"Return the corresponding OPTIMADE field name for the underlying database field, ready to be used to construct the OPTIMADE-compliant JSON response.
Aliases are read from all_aliases()
.
Parameters:
Name Type Description Defaultbackend_field
str
The backend field to attempt to map to an OPTIMADE field.
requiredExamples:
>>> get_optimade_field(\"chemical_formula_anonymous\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"formula_anon\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"custom_provider_field\")\n'_exmpl_custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in an OPTIMADE-compliant response.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_required_fields","title":"get_required_fields()
cached
classmethod
","text":"Get REQUIRED response fields.
Returns:
Type Descriptionset
REQUIRED response fields.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.length_alias_for","title":"length_alias_for(field)
cached
classmethod
","text":"Returns the length alias for the particular field, or None
if no such alias is found.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type DescriptionOptional[str]
Aliased field as found in all_length_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.map_back","title":"map_back(doc)
classmethod
","text":"Map properties from MongoDB to OPTIMADE
:param doc: A resource object in MongoDB format :type doc: dict
:return: A resource object in OPTIMADE format :rtype: dict
Source code inoptimade/server/mappers/links.py
@classmethod\ndef map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE\n\n :param doc: A resource object in MongoDB format\n :type doc: dict\n\n :return: A resource object in OPTIMADE format\n :rtype: dict\n \"\"\"\n type_ = doc[\"type\"]\n newdoc = super().map_back(doc)\n newdoc[\"type\"] = type_\n return newdoc\n
"},{"location":"api_reference/server/mappers/references/","title":"references","text":""},{"location":"api_reference/server/mappers/structures/","title":"structures","text":""},{"location":"api_reference/server/routers/index_info/","title":"index_info","text":""},{"location":"api_reference/server/routers/info/","title":"info","text":""},{"location":"api_reference/server/routers/landing/","title":"landing","text":"OPTIMADE landing page router.
"},{"location":"api_reference/server/routers/landing/#optimade.server.routers.landing.landing","title":"landing(request)
async
","text":"Show a human-readable landing page when the base URL is accessed.
Source code inoptimade/server/routers/landing.py
async def landing(request: Request):\n \"\"\"Show a human-readable landing page when the base URL is accessed.\"\"\"\n return render_landing_page(str(request.url))\n
"},{"location":"api_reference/server/routers/landing/#optimade.server.routers.landing.render_landing_page","title":"render_landing_page(url)
cached
","text":"Render and cache the landing page.
This function uses the template file ./static/landing_page.html
, adapted from the original Jinja template. Instead of Jinja, some basic string replacement is used to fill out the fields from the server configuration.
Careful
The removal of Jinja means that the fields are no longer validated as web safe before inclusion in the template.
Source code inoptimade/server/routers/landing.py
@lru_cache\ndef render_landing_page(url: str) -> HTMLResponse:\n \"\"\"Render and cache the landing page.\n\n This function uses the template file `./static/landing_page.html`, adapted\n from the original Jinja template. Instead of Jinja, some basic string\n replacement is used to fill out the fields from the server configuration.\n\n !!! warning \"Careful\"\n The removal of Jinja means that the fields are no longer validated as\n web safe before inclusion in the template.\n\n \"\"\"\n meta = meta_values(url, 1, 1, more_data_available=False, schema=CONFIG.schema_url)\n major_version = __api_version__.split(\".\")[0]\n versioned_url = f\"{get_base_url(url)}/v{major_version}/\"\n\n if CONFIG.custom_landing_page:\n html = Path(CONFIG.custom_landing_page).resolve().read_text()\n else:\n template_dir = Path(__file__).parent.joinpath(\"static\").resolve()\n html = (template_dir / \"landing_page.html\").read_text()\n\n # Build a dictionary that maps the old Jinja keys to the new simplified replacements\n replacements = {\n \"api_version\": __api_version__,\n }\n\n if meta.provider:\n replacements.update(\n {\n \"provider.name\": meta.provider.name,\n \"provider.prefix\": meta.provider.prefix,\n \"provider.description\": meta.provider.description,\n \"provider.homepage\": str(meta.provider.homepage) or \"\",\n }\n )\n\n if meta.implementation:\n replacements.update(\n {\n \"implementation.name\": meta.implementation.name or \"\",\n \"implementation.version\": meta.implementation.version or \"\",\n \"implementation.source_url\": str(meta.implementation.source_url or \"\"),\n }\n )\n\n for replacement in replacements:\n html = html.replace(f\"{{{{ {replacement} }}}}\", replacements[replacement])\n\n # Build the list of endpoints. The template already opens and closes the `<ul>` tag.\n endpoints_list = [\n f'<li><a href=\"{versioned_url}{endp}\">{versioned_url}{endp}</a></li>'\n for endp in list(ENTRY_COLLECTIONS.keys()) + [\"info\"]\n ]\n html = html.replace(\"{% ENDPOINTS %}\", \"\\n\".join(endpoints_list))\n\n # If the index base URL has been configured, also list it\n index_base_url_html = \"\"\n if CONFIG.index_base_url:\n index_base_url_html = f\"\"\"<h3>Index base URL:</h3>\n<p><a href={CONFIG.index_base_url}>{CONFIG.index_base_url}</a></p>\n\"\"\"\n html = html.replace(\"{% INDEX_BASE_URL %}\", index_base_url_html)\n\n return HTMLResponse(html)\n
"},{"location":"api_reference/server/routers/links/","title":"links","text":""},{"location":"api_reference/server/routers/references/","title":"references","text":""},{"location":"api_reference/server/routers/structures/","title":"structures","text":""},{"location":"api_reference/server/routers/utils/","title":"utils","text":""},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.JSONAPIResponse","title":"JSONAPIResponse
","text":" Bases: JSONResponse
This class simply patches fastapi.responses.JSONResponse
to use the JSON:API 'application/vnd.api+json' MIME type.
optimade/server/routers/utils.py
class JSONAPIResponse(JSONResponse):\n \"\"\"This class simply patches `fastapi.responses.JSONResponse` to use the\n JSON:API 'application/vnd.api+json' MIME type.\n\n \"\"\"\n\n media_type = \"application/vnd.api+json\"\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_base_url","title":"get_base_url(parsed_url_request)
","text":"Get base URL for current server
Take the base URL from the config file, if it exists, otherwise use the request.
Source code inoptimade/server/routers/utils.py
def get_base_url(\n parsed_url_request: Union[\n urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str\n ],\n) -> str:\n \"\"\"Get base URL for current server\n\n Take the base URL from the config file, if it exists, otherwise use the request.\n \"\"\"\n parsed_url_request = (\n urllib.parse.urlparse(parsed_url_request)\n if isinstance(parsed_url_request, str)\n else parsed_url_request\n )\n return (\n CONFIG.base_url.rstrip(\"/\")\n if CONFIG.base_url\n else f\"{parsed_url_request.scheme}://{parsed_url_request.netloc}\"\n )\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_entries","title":"get_entries(collection, request, params)
","text":"Generalized /{entry} endpoint getter
Source code inoptimade/server/routers/utils.py
def get_entries(\n collection: EntryCollection,\n request: Request,\n params: EntryListingQueryParams,\n) -> dict[str, Any]:\n \"\"\"Generalized /{entry} endpoint getter\"\"\"\n from optimade.server.routers import ENTRY_COLLECTIONS\n\n params.check_params(request.query_params)\n (\n results,\n data_returned,\n more_data_available,\n fields,\n include_fields,\n ) = collection.find(params)\n\n include = []\n if getattr(params, \"include\", False):\n include.extend(params.include.split(\",\"))\n\n included = []\n if results is not None:\n included = get_included_relationships(results, ENTRY_COLLECTIONS, include)\n\n if more_data_available:\n # Deduce the `next` link from the current request\n query = urllib.parse.parse_qs(request.url.query)\n query.update(collection.get_next_query_params(params, results))\n\n urlencoded = urllib.parse.urlencode(query, doseq=True)\n base_url = get_base_url(request.url)\n\n links = ToplevelLinks(next=f\"{base_url}{request.url.path}?{urlencoded}\")\n else:\n links = ToplevelLinks(next=None)\n\n if results is not None and (fields or include_fields):\n results = handle_response_fields(results, fields, include_fields) # type: ignore[assignment]\n\n return {\n \"links\": links,\n \"data\": results if results else [],\n \"meta\": meta_values(\n url=request.url,\n data_returned=data_returned,\n data_available=len(collection),\n more_data_available=more_data_available,\n schema=CONFIG.schema_url\n if not CONFIG.is_index\n else CONFIG.index_schema_url,\n ),\n \"included\": included,\n }\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_included_relationships","title":"get_included_relationships(results, ENTRY_COLLECTIONS, include_param)
","text":"Filters the included relationships and makes the appropriate compound request to include them in the response.
Parameters:
Name Type Description Defaultresults
Union[EntryResource, list[EntryResource], dict, list[dict]]
list of returned documents.
requiredENTRY_COLLECTIONS
dict[str, EntryCollection]
dictionary containing collections to query, with key based on endpoint type.
requiredinclude_param
list[str]
list of queried related resources that should be included in included
.
Returns:
Type Descriptionlist[Union[EntryResource, dict[str, Any]]]
Dictionary with the same keys as ENTRY_COLLECTIONS, each containing the list of resource objects for that entry type.
Source code inoptimade/server/routers/utils.py
def get_included_relationships(\n results: Union[EntryResource, list[EntryResource], dict, list[dict]],\n ENTRY_COLLECTIONS: dict[str, EntryCollection],\n include_param: list[str],\n) -> list[Union[EntryResource, dict[str, Any]]]:\n \"\"\"Filters the included relationships and makes the appropriate compound request\n to include them in the response.\n\n Parameters:\n results: list of returned documents.\n ENTRY_COLLECTIONS: dictionary containing collections to query, with key\n based on endpoint type.\n include_param: list of queried related resources that should be included in\n `included`.\n\n Returns:\n Dictionary with the same keys as ENTRY_COLLECTIONS, each containing the list\n of resource objects for that entry type.\n\n \"\"\"\n from collections import defaultdict\n\n if not isinstance(results, list):\n results = [results]\n\n for entry_type in include_param:\n if entry_type not in ENTRY_COLLECTIONS and entry_type != \"\":\n raise BadRequest(\n detail=f\"'{entry_type}' cannot be identified as a valid relationship type. \"\n f\"Known relationship types: {sorted(ENTRY_COLLECTIONS.keys())}\"\n )\n\n endpoint_includes: dict[Any, dict] = defaultdict(dict)\n for doc in results:\n # convert list of references into dict by ID to only included unique IDs\n if doc is None:\n continue\n\n try:\n relationships = doc.relationships # type: ignore\n except AttributeError:\n relationships = doc.get(\"relationships\", None)\n\n if relationships is None:\n continue\n\n if not isinstance(relationships, dict):\n relationships = relationships.model_dump()\n\n for entry_type in ENTRY_COLLECTIONS:\n # Skip entry type if it is not in `include_param`\n if entry_type not in include_param:\n continue\n\n entry_relationship = relationships.get(entry_type, {})\n if entry_relationship is not None:\n refs = entry_relationship.get(\"data\", [])\n for ref in refs:\n if ref[\"id\"] not in endpoint_includes[entry_type]:\n endpoint_includes[entry_type][ref[\"id\"]] = ref\n\n included: dict[\n str,\n Union[list[EntryResource], list[dict[str, Any]]],\n ] = {}\n for entry_type in endpoint_includes:\n compound_filter = \" OR \".join(\n [f'id=\"{ref_id}\"' for ref_id in endpoint_includes[entry_type]]\n )\n params = EntryListingQueryParams(\n filter=compound_filter,\n response_format=\"json\",\n response_fields=\"\",\n sort=\"\",\n page_limit=0,\n page_offset=0,\n )\n\n # still need to handle pagination\n ref_results, _, _, _, _ = ENTRY_COLLECTIONS[entry_type].find(params)\n if ref_results is None:\n ref_results = []\n included[entry_type] = ref_results # type: ignore[assignment]\n\n # flatten dict by endpoint to list\n return [obj for endp in included.values() for obj in endp]\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_providers","title":"get_providers(add_mongo_id=False)
","text":"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).
Fallback order if providers.optimade.org is not available:
providers
' list of providers./links
-endpoint.Parameters:
Name Type Description Defaultadd_mongo_id
bool
Whether to populate the _id
field of the provider with MongoDB ObjectID.
False
Returns:
Type Descriptionlist
List of raw JSON-decoded providers including MongoDB object IDs.
Source code inoptimade/utils.py
def get_providers(add_mongo_id: bool = False) -> list:\n \"\"\"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).\n\n Fallback order if providers.optimade.org is not available:\n\n 1. Try Materials-Consortia/providers on GitHub.\n 2. Try submodule `providers`' list of providers.\n 3. Log warning that providers list from Materials-Consortia is not included in the\n `/links`-endpoint.\n\n Arguments:\n add_mongo_id: Whether to populate the `_id` field of the provider with MongoDB\n ObjectID.\n\n Returns:\n List of raw JSON-decoded providers including MongoDB object IDs.\n\n \"\"\"\n import json\n\n import requests\n\n for provider_list_url in PROVIDER_LIST_URLS:\n try:\n providers = requests.get(provider_list_url, timeout=10).json()\n except (\n requests.exceptions.ConnectionError,\n requests.exceptions.ConnectTimeout,\n json.JSONDecodeError,\n requests.exceptions.SSLError,\n ):\n pass\n else:\n break\n else:\n try:\n from optimade.server.data import providers # type: ignore\n except ImportError:\n from optimade.server.logger import LOGGER\n\n LOGGER.warning(\n \"\"\"Could not retrieve a list of providers!\n\n Tried the following resources:\n\n{}\n The list of providers will not be included in the `/links`-endpoint.\n\"\"\".format(\"\".join([f\" * {_}\\n\" for _ in PROVIDER_LIST_URLS]))\n )\n return []\n\n providers_list = []\n for provider in providers.get(\"data\", []):\n # Remove/skip \"exmpl\"\n if provider[\"id\"] == \"exmpl\":\n continue\n\n provider.update(provider.pop(\"attributes\", {}))\n\n # Add MongoDB ObjectId\n if add_mongo_id:\n provider[\"_id\"] = {\n \"$oid\": mongo_id_for_database(provider[\"id\"], provider[\"type\"])\n }\n\n providers_list.append(provider)\n\n return providers_list\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.handle_response_fields","title":"handle_response_fields(results, exclude_fields, include_fields)
","text":"Handle query parameter response_fields
.
It is assumed that all fields are under attributes
. This is due to all other top-level fields are REQUIRED in the response.
Parameters:
Name Type Description Defaultexclude_fields
set[str]
Fields under attributes
to be excluded from the response.
include_fields
set[str]
Fields under attributes
that were requested that should be set to null if missing in the entry.
Returns:
Type Descriptionlist[dict[str, Any]]
List of resulting resources as dictionaries after pruning according to
list[dict[str, Any]]
the response_fields
OPTIMADE URL query parameter.
optimade/server/routers/utils.py
def handle_response_fields(\n results: Union[list[EntryResource], EntryResource, list[dict], dict],\n exclude_fields: set[str],\n include_fields: set[str],\n) -> list[dict[str, Any]]:\n \"\"\"Handle query parameter `response_fields`.\n\n It is assumed that all fields are under `attributes`.\n This is due to all other top-level fields are REQUIRED in the response.\n\n Parameters:\n exclude_fields: Fields under `attributes` to be excluded from the response.\n include_fields: Fields under `attributes` that were requested that should be\n set to null if missing in the entry.\n\n Returns:\n List of resulting resources as dictionaries after pruning according to\n the `response_fields` OPTIMADE URL query parameter.\n\n \"\"\"\n if not isinstance(results, list):\n results = [results]\n\n new_results = []\n while results:\n new_entry = results.pop(0)\n try:\n new_entry = new_entry.model_dump(exclude_unset=True, by_alias=True) # type: ignore[union-attr]\n except AttributeError:\n pass\n\n # Remove fields excluded by their omission in `response_fields`\n for field in exclude_fields:\n if field in new_entry[\"attributes\"]:\n del new_entry[\"attributes\"][field]\n\n # Include missing fields that were requested in `response_fields`\n for field in include_fields:\n if field not in new_entry[\"attributes\"]:\n new_entry[\"attributes\"][field] = None\n\n new_results.append(new_entry)\n\n return new_results\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.meta_values","title":"meta_values(url, data_returned, data_available, more_data_available, schema=None, **kwargs)
","text":"Helper to initialize the meta values
Source code inoptimade/server/routers/utils.py
def meta_values(\n url: Union[urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str],\n data_returned: Optional[int],\n data_available: int,\n more_data_available: bool,\n schema: Optional[str] = None,\n **kwargs,\n) -> ResponseMeta:\n \"\"\"Helper to initialize the meta values\"\"\"\n from optimade.models import ResponseMetaQuery\n\n if isinstance(url, str):\n url = urllib.parse.urlparse(url)\n\n # To catch all (valid) variations of the version part of the URL, a regex is used\n if re.match(r\"/v[0-9]+(\\.[0-9]+){,2}/.*\", url.path) is not None:\n url_path = re.sub(r\"/v[0-9]+(\\.[0-9]+){,2}/\", \"/\", url.path)\n else:\n url_path = url.path\n\n if schema is None:\n schema = CONFIG.schema_url if not CONFIG.is_index else CONFIG.index_schema_url\n\n return ResponseMeta(\n query=ResponseMetaQuery(representation=f\"{url_path}?{url.query}\"),\n api_version=__api_version__,\n time_stamp=datetime.now(),\n data_returned=data_returned,\n more_data_available=more_data_available,\n provider=CONFIG.provider,\n data_available=data_available,\n implementation=CONFIG.implementation,\n schema=schema,\n **kwargs,\n )\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.mongo_id_for_database","title":"mongo_id_for_database(database_id, database_type)
","text":"Produce a MongoDB ObjectId for a database
Source code inoptimade/utils.py
def mongo_id_for_database(database_id: str, database_type: str) -> str:\n \"\"\"Produce a MongoDB ObjectId for a database\"\"\"\n from bson.objectid import ObjectId\n\n oid = f\"{database_id}{database_type}\"\n if len(oid) > 12:\n oid = oid[:12]\n elif len(oid) < 12:\n oid = f\"{oid}{'0' * (12 - len(oid))}\"\n\n return str(ObjectId(oid.encode(\"UTF-8\")))\n
"},{"location":"api_reference/server/routers/versions/","title":"versions","text":""},{"location":"api_reference/server/routers/versions/#optimade.server.routers.versions.get_versions","title":"get_versions()
","text":"Respond with the text/csv representation for the served versions.
Source code inoptimade/server/routers/versions.py
@router.get(\n \"/versions\",\n tags=[\"Versions\"],\n response_class=CsvResponse,\n)\ndef get_versions() -> CsvResponse:\n \"\"\"Respond with the text/csv representation for the served versions.\"\"\"\n version = BASE_URL_PREFIXES[\"major\"].replace(\"/v\", \"\")\n response = f\"version\\n{version}\"\n return CsvResponse(content=response)\n
"},{"location":"api_reference/validator/config/","title":"config","text":"This submodule defines constant values and definitions from the OPTIMADE specification for use by the validator.
The VALIDATOR_CONFIG
object can be imported and modified before calling the validator inside a Python script to customise the hardcoded values.
ValidatorConfig
","text":" Bases: BaseSettings
This class stores validator config parameters in a way that can be easily modified for testing niche implementations. Many of these fields are determined by the specification directly, but it may be desirable to modify them in certain cases.
Source code inoptimade/validator/config.py
class ValidatorConfig(BaseSettings):\n \"\"\"This class stores validator config parameters in a way that\n can be easily modified for testing niche implementations. Many\n of these fields are determined by the specification directly,\n but it may be desirable to modify them in certain cases.\n\n \"\"\"\n\n response_classes: dict[str, Any] = Field(\n _RESPONSE_CLASSES,\n description=\"Dictionary containing the mapping between endpoints and response classes for the main database\",\n )\n\n response_classes_index: dict[str, Any] = Field(\n _RESPONSE_CLASSES_INDEX,\n description=\"Dictionary containing the mapping between endpoints and response classes for the index meta-database\",\n )\n\n entry_schemas: dict[str, Any] = Field(\n _ENTRY_SCHEMAS, description=\"The entry listing endpoint schemas\"\n )\n\n entry_endpoints: set[str] = Field(\n _ENTRY_ENDPOINTS,\n description=\"The entry endpoints to validate, if present in the API's `/info` response `entry_types_by_format['json']`\",\n )\n\n unique_properties: set[str] = Field(\n _UNIQUE_PROPERTIES,\n description=(\n \"Fields that should be treated as unique indexes for all endpoints, \"\n \"i.e. fields on which filters should return at most one entry.\"\n ),\n )\n\n inclusive_operators: dict[DataType, set[str]] = Field(\n _INCLUSIVE_OPERATORS,\n description=(\n \"Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'inclusive', \"\n \"i.e. those that should return entries with the matching value from the filter.\"\n ),\n )\n\n exclusive_operators: dict[DataType, set[str]] = Field(\n _EXCLUSIVE_OPERATORS,\n description=(\n \"Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'exclusive', \"\n \"i.e. those that should not return entries with the matching value from the filter.\"\n ),\n )\n\n field_specific_overrides: dict[str, dict[SupportLevel, Container[str]]] = Field(\n _FIELD_SPECIFIC_OVERRIDES,\n description=(\n \"Some fields do not require all type comparison operators to be supported. \"\n \"This dictionary allows overriding the list of supported operators for a field, using \"\n \"the field name as a key, and the support level of different operators with a subkey. \"\n \"Queries on fields listed in this way will pass the validator provided the server returns a 501 status.\"\n ),\n )\n\n links_endpoint: str = Field(\"links\", description=\"The name of the links endpoint\")\n versions_endpoint: str = Field(\n \"versions\", description=\"The name of the versions endpoint\"\n )\n\n info_endpoint: str = Field(\"info\", description=\"The name of the info endpoint\")\n non_entry_endpoints: set[str] = Field(\n _NON_ENTRY_ENDPOINTS,\n description=\"The list specification-mandated endpoint names that do not contain entries\",\n )\n top_level_non_attribute_fields: set[str] = Field(\n BaseResourceMapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS,\n description=\"Field names to treat as top-level\",\n )\n\n enum_fallback_values: dict[str, dict[str, list[str]]] = Field(\n _ENUM_DUMMY_VALUES,\n description=\"Provide fallback values for enum fields to use when validating filters.\",\n )\n
"},{"location":"api_reference/validator/utils/","title":"utils","text":"This submodule contains utility methods and models used by the validator. The two main features being:
@test_case
decorator can be used to decorate validation methods and performs error handling, output and logging of test successes and failures.Validator
versions allow for stricter validation of server responses. The standard response classes allow entries to be provided as bare dictionaries, whilst these patched classes force them to be validated with the corresponding entry models themselves.Client
","text":"Source code in optimade/validator/utils.py
class Client: # pragma: no cover\n def __init__(\n self,\n base_url: str,\n max_retries: int = 5,\n headers: Optional[dict[str, str]] = None,\n timeout: Optional[float] = DEFAULT_CONN_TIMEOUT,\n read_timeout: Optional[float] = DEFAULT_READ_TIMEOUT,\n ) -> None:\n \"\"\"Initialises the Client with the given `base_url` without testing\n if it is valid.\n\n Parameters:\n base_url: the base URL of the optimade implementation, including\n request protocol (e.g. `'http://'`) and API version number if necessary.\n\n Examples:\n\n - `'http://example.org/optimade/v1'`,\n - `'www.crystallography.net/cod-test/optimade/v0.10.0/'`\n\n Note: A maximum of one slash (\"/\") is allowed as the last character.\n\n max_retries: The maximum number of attempts to make for each query.\n headers: Dictionary of additional headers to add to every request.\n timeout: Connection timeout in seconds.\n read_timeout: Read timeout in seconds.\n\n \"\"\"\n self.base_url: str = base_url\n self.last_request: Optional[str] = None\n self.response: Optional[requests.Response] = None\n self.max_retries = max_retries\n self.headers = headers or {}\n if \"User-Agent\" not in self.headers:\n self.headers[\"User-Agent\"] = DEFAULT_USER_AGENT_STRING\n self.timeout = timeout or DEFAULT_CONN_TIMEOUT\n self.read_timeout = read_timeout or DEFAULT_READ_TIMEOUT\n\n def get(self, request: str):\n \"\"\"Makes the given request, with a number of retries if being rate limited. The\n request will be prepended with the `base_url` unless the request appears to be an\n absolute URL (i.e. starts with `http://` or `https://`).\n\n Parameters:\n request (str): the request to make against the base URL of this client.\n\n Returns:\n response (requests.models.Response): the response from the server.\n\n Raises:\n SystemExit: if there is no response from the server, or if the URL is invalid.\n ResponseError: if the server does not respond with a non-429 status code within\n the `MAX_RETRIES` attempts.\n\n \"\"\"\n if urllib.parse.urlparse(request, allow_fragments=True).scheme:\n self.last_request = request\n else:\n if request and not request.startswith(\"/\"):\n request = f\"/{request}\"\n self.last_request = f\"{self.base_url}{request}\"\n\n status_code = None\n retries = 0\n errors = []\n while retries < self.max_retries:\n retries += 1\n try:\n self.response = requests.get(\n self.last_request,\n headers=self.headers,\n timeout=(self.timeout, self.read_timeout),\n )\n\n status_code = self.response.status_code\n # If we hit a 429 Too Many Requests status, then try again in 1 second\n if status_code != 429:\n return self.response\n\n # If the connection times out, retry but cache the error\n except requests.exceptions.ConnectionError as exc:\n errors.append(str(exc))\n\n # Read timeouts should prevent further retries\n except requests.exceptions.ReadTimeout as exc:\n raise ResponseError(str(exc)) from exc\n\n except requests.exceptions.MissingSchema:\n sys.exit(\n f\"Unable to make request on {self.last_request}, did you mean http://{self.last_request}?\"\n )\n\n # If the connection failed, or returned a 429, then wait 1 second before retrying\n time.sleep(1)\n\n else:\n message = f\"Hit max retries ({self.max_retries}) on request {self.last_request!r}.\"\n if errors:\n error_str = \"\\n\\t\".join(errors)\n message += f\"\\nErrors:\\n\\t{error_str}\"\n raise ResponseError(message)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.Client.__init__","title":"__init__(base_url, max_retries=5, headers=None, timeout=DEFAULT_CONN_TIMEOUT, read_timeout=DEFAULT_READ_TIMEOUT)
","text":"Initialises the Client with the given base_url
without testing if it is valid.
Parameters:
Name Type Description Defaultbase_url
str
the base URL of the optimade implementation, including request protocol (e.g. 'http://'
) and API version number if necessary.
Examples:
'http://example.org/optimade/v1'
,'www.crystallography.net/cod-test/optimade/v0.10.0/'
Note: A maximum of one slash (\"/\") is allowed as the last character.
requiredmax_retries
int
The maximum number of attempts to make for each query.
5
headers
Optional[dict[str, str]]
Dictionary of additional headers to add to every request.
None
timeout
Optional[float]
Connection timeout in seconds.
DEFAULT_CONN_TIMEOUT
read_timeout
Optional[float]
Read timeout in seconds.
DEFAULT_READ_TIMEOUT
Source code in optimade/validator/utils.py
def __init__(\n self,\n base_url: str,\n max_retries: int = 5,\n headers: Optional[dict[str, str]] = None,\n timeout: Optional[float] = DEFAULT_CONN_TIMEOUT,\n read_timeout: Optional[float] = DEFAULT_READ_TIMEOUT,\n) -> None:\n \"\"\"Initialises the Client with the given `base_url` without testing\n if it is valid.\n\n Parameters:\n base_url: the base URL of the optimade implementation, including\n request protocol (e.g. `'http://'`) and API version number if necessary.\n\n Examples:\n\n - `'http://example.org/optimade/v1'`,\n - `'www.crystallography.net/cod-test/optimade/v0.10.0/'`\n\n Note: A maximum of one slash (\"/\") is allowed as the last character.\n\n max_retries: The maximum number of attempts to make for each query.\n headers: Dictionary of additional headers to add to every request.\n timeout: Connection timeout in seconds.\n read_timeout: Read timeout in seconds.\n\n \"\"\"\n self.base_url: str = base_url\n self.last_request: Optional[str] = None\n self.response: Optional[requests.Response] = None\n self.max_retries = max_retries\n self.headers = headers or {}\n if \"User-Agent\" not in self.headers:\n self.headers[\"User-Agent\"] = DEFAULT_USER_AGENT_STRING\n self.timeout = timeout or DEFAULT_CONN_TIMEOUT\n self.read_timeout = read_timeout or DEFAULT_READ_TIMEOUT\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.Client.get","title":"get(request)
","text":"Makes the given request, with a number of retries if being rate limited. The request will be prepended with the base_url
unless the request appears to be an absolute URL (i.e. starts with http://
or https://
).
Parameters:
Name Type Description Defaultrequest
str
the request to make against the base URL of this client.
requiredReturns:
Name Type Descriptionresponse
Response
the response from the server.
Raises:
Type DescriptionSystemExit
if there is no response from the server, or if the URL is invalid.
ResponseError
if the server does not respond with a non-429 status code within the MAX_RETRIES
attempts.
optimade/validator/utils.py
def get(self, request: str):\n \"\"\"Makes the given request, with a number of retries if being rate limited. The\n request will be prepended with the `base_url` unless the request appears to be an\n absolute URL (i.e. starts with `http://` or `https://`).\n\n Parameters:\n request (str): the request to make against the base URL of this client.\n\n Returns:\n response (requests.models.Response): the response from the server.\n\n Raises:\n SystemExit: if there is no response from the server, or if the URL is invalid.\n ResponseError: if the server does not respond with a non-429 status code within\n the `MAX_RETRIES` attempts.\n\n \"\"\"\n if urllib.parse.urlparse(request, allow_fragments=True).scheme:\n self.last_request = request\n else:\n if request and not request.startswith(\"/\"):\n request = f\"/{request}\"\n self.last_request = f\"{self.base_url}{request}\"\n\n status_code = None\n retries = 0\n errors = []\n while retries < self.max_retries:\n retries += 1\n try:\n self.response = requests.get(\n self.last_request,\n headers=self.headers,\n timeout=(self.timeout, self.read_timeout),\n )\n\n status_code = self.response.status_code\n # If we hit a 429 Too Many Requests status, then try again in 1 second\n if status_code != 429:\n return self.response\n\n # If the connection times out, retry but cache the error\n except requests.exceptions.ConnectionError as exc:\n errors.append(str(exc))\n\n # Read timeouts should prevent further retries\n except requests.exceptions.ReadTimeout as exc:\n raise ResponseError(str(exc)) from exc\n\n except requests.exceptions.MissingSchema:\n sys.exit(\n f\"Unable to make request on {self.last_request}, did you mean http://{self.last_request}?\"\n )\n\n # If the connection failed, or returned a 429, then wait 1 second before retrying\n time.sleep(1)\n\n else:\n message = f\"Hit max retries ({self.max_retries}) on request {self.last_request!r}.\"\n if errors:\n error_str = \"\\n\\t\".join(errors)\n message += f\"\\nErrors:\\n\\t{error_str}\"\n raise ResponseError(message)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.InternalError","title":"InternalError
","text":" Bases: Exception
This exception should be raised when validation throws an unexpected error. These should be counted separately from ResponseError
's and ValidationError
's.
optimade/validator/utils.py
class InternalError(Exception):\n \"\"\"This exception should be raised when validation throws an unexpected error.\n These should be counted separately from `ResponseError`'s and `ValidationError`'s.\n\n \"\"\"\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.ResponseError","title":"ResponseError
","text":" Bases: Exception
This exception should be raised for a manual hardcoded test failure.
Source code inoptimade/validator/utils.py
class ResponseError(Exception):\n \"\"\"This exception should be raised for a manual hardcoded test failure.\"\"\"\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_failure","title":"print_failure(string, **kwargs)
","text":"Print but sad.
Source code inoptimade/validator/utils.py
def print_failure(string: str, **kwargs) -> None:\n \"\"\"Print but sad.\"\"\"\n print(f\"\\033[91m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_notify","title":"print_notify(string, **kwargs)
","text":"Print but louder.
Source code inoptimade/validator/utils.py
def print_notify(string: str, **kwargs) -> None:\n \"\"\"Print but louder.\"\"\"\n print(f\"\\033[94m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_success","title":"print_success(string, **kwargs)
","text":"Print but happy.
Source code inoptimade/validator/utils.py
def print_success(string: str, **kwargs) -> None:\n \"\"\"Print but happy.\"\"\"\n print(f\"\\033[92m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_warning","title":"print_warning(string, **kwargs)
","text":"Print but angry.
Source code inoptimade/validator/utils.py
def print_warning(string: str, **kwargs) -> None:\n \"\"\"Print but angry.\"\"\"\n print(f\"\\033[93m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.test_case","title":"test_case(test_fn)
","text":"Wrapper for test case functions, which pretty-prints any errors depending on verbosity level, collates the number and severity of test failures, returns the response and summary string to the caller. Any additional positional or keyword arguments are passed directly to test_fn
. The wrapper will intercept the named arguments optional
, multistage
and request
and interpret them according to the docstring for wrapper(...)
below.
Parameters:
Name Type Description Defaulttest_fn
Callable[..., tuple[Any, str]]
Any function that returns an object and a message to print upon success. The function should raise a ResponseError
, ValidationError
or a ManualValidationError
if the test case has failed. The function can return None
to indicate that the test was not appropriate and should be ignored.
optimade/validator/utils.py
def test_case(test_fn: Callable[..., tuple[Any, str]]):\n \"\"\"Wrapper for test case functions, which pretty-prints any errors\n depending on verbosity level, collates the number and severity of\n test failures, returns the response and summary string to the caller.\n Any additional positional or keyword arguments are passed directly\n to `test_fn`. The wrapper will intercept the named arguments\n `optional`, `multistage` and `request` and interpret them according\n to the docstring for `wrapper(...)` below.\n\n Parameters:\n test_fn: Any function that returns an object and a message to\n print upon success. The function should raise a `ResponseError`,\n `ValidationError` or a `ManualValidationError` if the test\n case has failed. The function can return `None` to indicate\n that the test was not appropriate and should be ignored.\n\n \"\"\"\n from functools import wraps\n\n @wraps(test_fn)\n def wrapper(\n validator,\n *args,\n request: Optional[str] = None,\n optional: bool = False,\n multistage: bool = False,\n **kwargs,\n ):\n \"\"\"Wraps a function or validator method and handles\n success, failure and output depending on the keyword\n arguments passed.\n\n Arguments:\n validator: The validator object to accumulate errors/counters.\n *args: Positional arguments passed to the test function.\n request: Description of the request made by the wrapped\n function (e.g. a URL or a summary).\n optional: Whether or not to treat the test as optional.\n multistage: If `True`, no output will be printed for this test,\n and it will not increment the success counter. Errors will be\n handled in the normal way. This can be used to avoid flooding\n the output for mutli-stage tests.\n **kwargs: Extra named arguments passed to the test function.\n\n \"\"\"\n try:\n try:\n if optional and not validator.run_optional_tests:\n result = None\n msg = \"skipping optional\"\n else:\n result, msg = test_fn(validator, *args, **kwargs)\n\n except (json.JSONDecodeError, ResponseError, ValidationError) as exc:\n msg = f\"{exc.__class__.__name__}: {exc}\"\n raise exc\n except Exception as exc:\n msg = f\"{exc.__class__.__name__}: {exc}\"\n raise InternalError(msg)\n\n # Catch SystemExit and KeyboardInterrupt explicitly so that we can pass\n # them to the finally block, where they are immediately raised\n except (Exception, SystemExit, KeyboardInterrupt) as exc:\n result = exc\n traceback = tb.format_exc()\n\n finally:\n # This catches the case of the Client throwing a SystemExit if the server\n # did not respond, the case of the validator \"fail-fast\"'ing and throwing\n # a SystemExit below, and the case of the user interrupting the process manually\n if isinstance(result, (SystemExit, KeyboardInterrupt)):\n raise result\n\n display_request = None\n try:\n display_request = validator.client.last_request\n except AttributeError:\n pass\n if display_request is None:\n display_request = validator.base_url\n if request is not None:\n display_request += \"/\" + request\n\n request = display_request\n\n # If the result was None, return it here and ignore statuses\n if result is None:\n return result, msg\n display_request = requests.utils.requote_uri(request.replace(\"\\n\", \"\")) # type: ignore[union-attr]\n\n if not isinstance(result, Exception):\n if not multistage:\n success_type = \"optional\" if optional else None\n validator.results.add_success(\n f\"{display_request} - {msg}\", success_type\n )\n else:\n message = msg.split(\"\\n\")\n if validator.verbosity > 1:\n # ValidationErrors from pydantic already include very detailed errors\n # that get duplicated in the traceback\n if not isinstance(result, ValidationError):\n message += traceback.split(\"\\n\")\n\n failure_type: Optional[str] = None\n if isinstance(result, InternalError):\n summary = f\"{display_request} - {test_fn.__name__} - failed with internal error\"\n failure_type = \"internal\"\n else:\n summary = (\n f\"{display_request} - {test_fn.__name__} - failed with error\"\n )\n failure_type = \"optional\" if optional else None\n\n validator.results.add_failure(\n summary, \"\\n\".join(message), failure_type=failure_type\n )\n\n # set failure result to None as this is expected by other functions\n result = None\n\n if validator.fail_fast and not optional:\n validator.print_summary()\n raise SystemExit\n\n # Reset the client request so that it can be properly\n # displayed if the next request fails\n if not multistage:\n validator.client.last_request = None\n\n return result, msg\n\n return wrapper\n
"},{"location":"api_reference/validator/validator/","title":"validator","text":"This module contains the ImplementationValidator
class that can be pointed at an OPTIMADE implementation and validated against the specification via the pydantic models implemented in this package.
ImplementationValidator
","text":"Class used to make a series of checks against a particular OPTIMADE implementation over HTTP.
Uses the pydantic models in optimade.models
to validate the response from the server and crawl through the available endpoints.
Attributes:
Name Type Descriptionvalid
Optional[bool]
whether or not the implementation was deemed valid, with None
signifying that tests did not run.
Only works for current version of the specification as defined by optimade.models
.
optimade/validator/validator.py
class ImplementationValidator:\n \"\"\"Class used to make a series of checks against a particular\n OPTIMADE implementation over HTTP.\n\n Uses the pydantic models in [`optimade.models`][optimade.models] to\n validate the response from the server and crawl through the\n available endpoints.\n\n Attributes:\n valid: whether or not the implementation was deemed valid, with\n `None` signifying that tests did not run.\n\n Caution:\n Only works for current version of the specification as defined\n by [`optimade.models`][optimade.models].\n\n \"\"\"\n\n valid: Optional[bool]\n\n def __init__(\n self,\n client: Optional[Any] = None,\n base_url: Optional[str] = None,\n verbosity: int = 0,\n respond_json: bool = False,\n page_limit: int = 4,\n max_retries: int = 5,\n run_optional_tests: bool = True,\n fail_fast: bool = False,\n as_type: Optional[str] = None,\n index: bool = False,\n minimal: bool = False,\n http_headers: Optional[dict[str, str]] = None,\n timeout: float = DEFAULT_CONN_TIMEOUT,\n read_timeout: float = DEFAULT_READ_TIMEOUT,\n ):\n \"\"\"Set up the tests to run, based on constants in this module\n for required endpoints.\n\n Arguments:\n client: A client that has a `.get()` method to obtain the\n response from the implementation. If `None`, then\n [`Client`][optimade.validator.utils.Client] will be used.\n base_url: The URL of the implementation to validate. Unless\n performing \"as_type\" validation, this should point to the\n base of the OPTIMADE implementation.\n verbosity: The verbosity of the output and logging as an integer\n (`0`: critical, `1`: warning, `2`: info, `3`: debug).\n respond_json: If `True`, print only a JSON representation of the\n results of validation to stdout.\n page_limit: The default page limit to apply to filters.\n max_retries: Argument is passed to the client for how many\n attempts to make for a request before failing.\n run_optional_tests: Whether to run the tests on optional\n OPTIMADE features.\n fail_fast: Whether to exit validation after the first failure\n of a mandatory test.\n as_type: An OPTIMADE entry or endpoint type to coerce the response\n from implementation into, e.g. \"structures\". Requires `base_url`\n to be pointed to the corresponding endpoint.\n index: Whether to validate the implementation as an index meta-database.\n minimal: Whether or not to run only a minimal test set.\n http_headers: Dictionary of additional headers to add to every request.\n timeout: The connection timeout to use for all requests (in seconds).\n read_timeout: The read timeout to use for all requests (in seconds).\n\n \"\"\"\n self.verbosity = verbosity\n self.max_retries = max_retries\n self.page_limit = page_limit\n self.index = index\n self.run_optional_tests = run_optional_tests\n self.fail_fast = fail_fast\n self.respond_json = respond_json\n self.minimal = minimal\n\n if as_type is None:\n self.as_type_cls = None\n elif self.index:\n if as_type not in CONF.response_classes_index:\n raise RuntimeError(\n f\"Provided as_type='{as_type}' not allowed for an Index meta-database.\"\n )\n self.as_type_cls = CONF.response_classes_index[as_type]\n elif as_type in (\"structure\", \"reference\"):\n self.as_type_cls = CONF.response_classes[f\"{as_type}s/\"]\n else:\n self.as_type_cls = CONF.response_classes[as_type]\n\n if client is None and base_url is None:\n raise RuntimeError(\n \"Need at least a URL or a client to initialize validator.\"\n )\n if base_url and client:\n raise RuntimeError(\"Please specify at most one of base_url or client.\")\n if client:\n self.client = client\n self.base_url = self.client.base_url\n # If a custom client has been provided, try to set custom headers if they have been specified,\n # but do not overwrite any existing attributes\n if http_headers:\n if not hasattr(self.client, \"headers\"):\n self.client.headers = http_headers\n else:\n print_warning(\n f\"Not using specified request headers {http_headers} with custom client {self.client}.\"\n )\n else:\n while base_url.endswith(\"/\"): # type: ignore[union-attr]\n base_url = base_url[:-1] # type: ignore[index]\n self.base_url = base_url\n self.client = Client(\n self.base_url, # type: ignore[arg-type]\n max_retries=self.max_retries,\n headers=http_headers,\n timeout=timeout,\n read_timeout=read_timeout,\n )\n\n self._setup_log()\n\n self._response_classes = (\n CONF.response_classes_index if self.index else CONF.response_classes\n )\n\n # some simple checks on base_url\n self.base_url = str(self.base_url)\n self.base_url_parsed = urllib.parse.urlparse(self.base_url)\n # only allow filters/endpoints if we are working in \"as_type\" mode\n if self.as_type_cls is None and self.base_url_parsed.query:\n raise SystemExit(\n f\"Base URL {self.base_url} not appropriate: should not contain a filter.\"\n )\n\n self.valid = None\n\n self._test_id_by_type: dict[str, Any] = {}\n self._entry_info_by_type: dict[str, Any] = {}\n\n self.results = ValidatorResults(verbosity=self.verbosity)\n\n def _setup_log(self):\n \"\"\"Define stdout log based on given verbosity.\"\"\"\n self._log = logging.getLogger(\"optimade\").getChild(\"validator\")\n self._log.handlers = []\n stdout_handler = logging.StreamHandler(sys.stdout)\n stdout_handler.setFormatter(\n logging.Formatter(\"%(asctime)s - %(name)s | %(levelname)8s: %(message)s\")\n )\n\n if not self.respond_json:\n self._log.addHandler(stdout_handler)\n else:\n self.verbosity = -1\n\n if self.verbosity == 0:\n self._log.setLevel(logging.CRITICAL)\n elif self.verbosity == 1:\n self._log.setLevel(logging.WARNING)\n elif self.verbosity == 2:\n self._log.setLevel(logging.INFO)\n elif self.verbosity > 0:\n self._log.setLevel(logging.DEBUG)\n\n def print_summary(self):\n \"\"\"Print a summary of the results of validation.\"\"\"\n if self.respond_json:\n print(json.dumps(dataclasses.asdict(self.results), indent=2))\n return\n\n if self.results.failure_messages:\n print(\"\\n\\nFAILURES\")\n print(\"========\\n\")\n for message in self.results.failure_messages:\n print_failure(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.optional_failure_messages:\n print(\"\\n\\nOPTIONAL TEST FAILURES\")\n print(\"======================\\n\")\n for message in self.results.optional_failure_messages:\n print_notify(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.internal_failure_messages:\n print(\"\\n\\nINTERNAL FAILURES\")\n print(\"=================\\n\")\n print(\n \"There were internal validator failures associated with this run.\\n\"\n \"If this problem persists, please report it at:\\n\"\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues/new\\n\"\n )\n\n for message in self.results.internal_failure_messages:\n print_warning(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.valid or (not self.valid and not self.fail_fast):\n final_message = f\"\\n\\nPassed {self.results.success_count} out of {self.results.success_count + self.results.failure_count + self.results.internal_failure_count} tests.\"\n if not self.valid:\n print_failure(final_message)\n else:\n print_success(final_message)\n\n if self.run_optional_tests and not self.fail_fast:\n print(\n f\"Additionally passed {self.results.optional_success_count} out of \"\n f\"{self.results.optional_success_count + self.results.optional_failure_count} optional tests.\"\n )\n\n def validate_implementation(self):\n \"\"\"Run all the test cases on the implementation, or the single type test,\n depending on what options were provided on initialiation.\n\n Sets the `self.valid` attribute to `True` or `False` depending on the\n outcome of the tests.\n\n Raises:\n RuntimeError: If it was not possible to start the validation process.\n\n \"\"\"\n # If a single \"as type\" has been set, only run that test\n if self.as_type_cls is not None:\n self._log.debug(\n \"Validating response of %s with model %s\",\n self.base_url,\n self.as_type_cls,\n )\n self._test_as_type()\n self.valid = not bool(self.results.failure_count)\n self.print_summary()\n return\n\n # Test entire implementation\n if self.verbosity >= 0:\n print(f\"Testing entire implementation at {self.base_url}\")\n info_endp = CONF.info_endpoint\n self._log.debug(\"Testing base info endpoint of %s\", info_endp)\n\n # Get and validate base info to find endpoints\n # If this is not possible, then exit at this stage\n base_info = self._test_info_or_links_endpoint(info_endp)\n if not base_info:\n self._log.critical(\n f\"Unable to deserialize response from introspective {info_endp!r} endpoint. \"\n \"This is required for all further validation, so the validator will now exit.\"\n )\n # Set valid to False to ensure error code 1 is raised at CLI\n self.valid = False\n self.print_summary()\n return\n\n # Grab the provider prefix from base info and use it when looking for provider fields\n self.provider_prefix = None\n meta = base_info.get(\"meta\", {})\n if meta.get(\"provider\") is not None:\n self.provider_prefix = meta[\"provider\"].get(\"prefix\")\n\n # Set the response class for all `/info/entry` endpoints based on `/info` response\n self.available_json_endpoints, _ = self._get_available_endpoints(\n base_info, request=info_endp\n )\n for endp in self.available_json_endpoints:\n self._response_classes[f\"{info_endp}/{endp}\"] = EntryInfoResponse\n\n # Run some tests on the versions endpoint\n self._log.debug(\"Testing versions endpoint %s\", CONF.versions_endpoint)\n self._test_versions_endpoint()\n self._test_bad_version_returns_553()\n\n # Test that entry info endpoints deserialize correctly\n # If they do not, the corresponding entry in _entry_info_by_type\n # is set to False, which must be checked for further validation\n for endp in self.available_json_endpoints:\n entry_info_endpoint = f\"{info_endp}/{endp}\"\n self._log.debug(\"Testing expected info endpoint %s\", entry_info_endpoint)\n self._entry_info_by_type[endp] = self._test_info_or_links_endpoint(\n entry_info_endpoint\n )\n\n # Test that the results from multi-entry-endpoints obey, e.g. page limits,\n # and that all entries can be deserialized with the patched models.\n # These methods also set the test_ids for each type of entry, which are validated\n # in the next loop.\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing multiple entry endpoint of %s\", endp)\n self._test_multi_entry_endpoint(endp)\n\n # Test that the single IDs scraped earlier work with the single entry endpoint\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing single entry request of type %s\", endp)\n self._test_single_entry_endpoint(endp)\n\n # Use the _entry_info_by_type to construct filters on the relevant endpoints\n if not self.minimal:\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing queries on JSON entry endpoint of %s\", endp)\n self._recurse_through_endpoint(endp)\n\n # Test that the links endpoint can be serialized correctly\n self._log.debug(\"Testing %s endpoint\", CONF.links_endpoint)\n self._test_info_or_links_endpoint(CONF.links_endpoint)\n\n self.valid = not (\n self.results.failure_count or self.results.internal_failure_count\n )\n\n self.print_summary()\n\n @test_case\n def _recurse_through_endpoint(self, endp: str) -> tuple[Optional[bool], str]:\n \"\"\"For a given endpoint (`endp`), get the entry type\n and supported fields, testing that all mandatory fields\n are supported, then test queries on every property according\n to the reported type, with optionality decided by the\n specification-level support level for that field.\n\n Parameters:\n endp: Endpoint to be tested.\n\n Returns:\n `True` if endpoint passed the tests, and a string summary.\n\n \"\"\"\n entry_info = self._entry_info_by_type.get(endp)\n\n if not entry_info:\n raise ResponseError(\n f\"Unable to generate filters for endpoint {endp}: 'info/{endp}' response was malformed.\"\n )\n\n _impl_properties = self._check_entry_info(entry_info, endp)\n prop_list = list(_impl_properties.keys())\n\n self._check_response_fields(endp, prop_list)\n\n chosen_entry, _ = self._get_archetypal_entry(endp, prop_list)\n\n if not chosen_entry:\n return (\n None,\n f\"Unable to generate filters for endpoint {endp}: no valid entries found.\",\n )\n\n for prop in _impl_properties:\n # check support level of property\n prop_type = _impl_properties[prop][\"type\"]\n sortable = _impl_properties[prop][\"sortable\"]\n optional = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n == SupportLevel.OPTIONAL\n )\n\n if optional and not self.run_optional_tests:\n continue\n\n self._construct_queries_for_property(\n prop,\n prop_type,\n sortable,\n endp,\n chosen_entry,\n request=f\"; testing queries for {endp}->{prop}\",\n optional=optional,\n )\n\n self._test_unknown_provider_property(endp)\n self._test_completely_unknown_property(endp)\n\n return True, f\"successfully recursed through endpoint {endp}.\"\n\n @test_case\n def _test_completely_unknown_property(self, endp):\n request = f\"{endp}?filter=crazyfield = 2\"\n response, _ = self._get_endpoint(\n request,\n expected_status_code=400,\n )\n\n return True, \"unknown field returned 400 Bad Request, as expected\"\n\n @test_case\n def _test_unknown_provider_property(self, endp):\n dummy_provider_field = \"_crazyprovider_field\"\n\n request = f\"{endp}?filter={dummy_provider_field}=2\"\n response, _ = self._get_endpoint(\n request,\n multistage=True,\n request=request,\n )\n\n if response is not None:\n deserialized, _ = self._deserialize_response(\n response, CONF.response_classes[endp], request=request, multistage=True\n )\n\n return (\n True,\n \"Unknown provider field was ignored when filtering, as expected\",\n )\n\n raise ResponseError(\n \"Failed to handle field from unknown provider; should return without affecting filter results\"\n )\n\n def _check_entry_info(\n self, entry_info: dict[str, Any], endp: str\n ) -> dict[str, dict[str, Any]]:\n \"\"\"Checks that `entry_info` contains all the required properties,\n and returns the property list for the endpoint.\n\n Parameters:\n entry_info: JSON representation of the response from the\n entry info endpoint.\n endp: The name of the entry endpoint.\n\n Returns:\n The list of property names supported by this implementation.\n\n \"\"\"\n properties = entry_info.get(\"data\", {}).get(\"properties\", [])\n self._test_must_properties(\n properties, endp, request=f\"{CONF.info_endpoint}/{endp}\"\n )\n\n return properties\n\n @test_case\n def _test_must_properties(\n self, properties: list[str], endp: str\n ) -> tuple[bool, str]:\n \"\"\"Check that the entry info lists all properties with the \"MUST\"\n support level for this endpoint.\n\n Parameters:\n properties: The list of property names supported by the endpoint.\n endp: The endpoint.\n\n Returns:\n `True` if the properties were found, and a string summary.\n\n \"\"\"\n must_props = {\n prop\n for prop in CONF.entry_schemas.get(endp, {})\n if CONF.entry_schemas[endp].get(prop, {}).get(\"support\")\n == SupportLevel.MUST\n }\n must_props_supported = {prop for prop in properties if prop in must_props}\n missing = must_props - must_props_supported\n if len(missing) != 0:\n raise ResponseError(\n f\"Some 'MUST' properties were missing from info/{endp}: {missing}\"\n )\n\n return True, f\"Found all required properties in entry info for endpoint {endp}\"\n\n @test_case\n def _get_archetypal_entry(\n self, endp: str, properties: list[str]\n ) -> tuple[Optional[dict[str, Any]], str]:\n \"\"\"Get a random entry from the first page of results for this\n endpoint.\n\n Parameters:\n endp: The endpoint to query.\n\n Returns:\n The JSON representation of the chosen entry and the summary message.\n\n\n \"\"\"\n response, message = self._get_endpoint(endp, multistage=True)\n\n if response:\n data = response.json().get(\"data\", [])\n data_returned = len(data)\n if data_returned < 1:\n return (\n None,\n \"Endpoint {endp!r} returned no entries, cannot get archetypal entry or test filtering.\",\n )\n\n archetypal_entry = response.json()[\"data\"][\n random.randint(0, data_returned - 1)\n ]\n if \"id\" not in archetypal_entry:\n raise ResponseError(\n f\"Chosen archetypal entry did not have an ID, cannot proceed: {archetypal_entry!r}\"\n )\n\n return (\n archetypal_entry,\n f\"set archetypal entry for {endp} with ID {archetypal_entry['id']}.\",\n )\n\n raise ResponseError(f\"Failed to get archetypal entry. Details: {message}\")\n\n @test_case\n def _check_response_fields(\n self, endp: str, fields: list[str]\n ) -> tuple[Optional[bool], str]:\n \"\"\"Check that the response field query parameter is obeyed.\n\n Parameters:\n endp: The endpoint to query.\n fields: The known fields for this endpoint to test.\n\n Returns:\n Bool indicating success and a summary message.\n\n \"\"\"\n\n subset_fields = random.sample(fields, min(len(fields) - 1, 3))\n test_query = f\"{endp}?response_fields={','.join(subset_fields)}&page_limit=1\"\n response, _ = self._get_endpoint(test_query, multistage=True)\n\n if response and len(response.json()[\"data\"]) > 0:\n doc = response.json()[\"data\"][0]\n expected_fields = set(subset_fields)\n expected_fields -= CONF.top_level_non_attribute_fields\n\n if \"attributes\" not in doc:\n raise ResponseError(\n f\"Entries are missing `attributes` key.\\nReceived: {doc}\"\n )\n\n returned_fields = set(sorted(list(doc.get(\"attributes\", {}).keys())))\n returned_fields -= CONF.top_level_non_attribute_fields\n\n if expected_fields != returned_fields:\n raise ResponseError(\n f\"Response fields not obeyed by {endp!r}:\\nExpected: {expected_fields}\\nReturned: {returned_fields}\"\n )\n\n return True, \"Successfully limited response fields\"\n\n return (\n None,\n f\"Unable to test adherence to response fields as no entries were returned for endpoint {endp!r}.\",\n )\n\n @test_case\n def _construct_queries_for_property(\n self,\n prop: str,\n prop_type: DataType,\n sortable: bool,\n endp: str,\n chosen_entry: dict[str, Any],\n ) -> tuple[Optional[bool], str]:\n \"\"\"For the given property, property type and chose entry, this method\n runs a series of queries for each field in the entry, testing that the\n initial document is returned where expected.\n\n\n Parameters:\n prop: The property name.\n prop_type: The property type.\n sortable: Whether the implementation has indicated that the field is sortable.\n endp: The corresponding entry endpoint.\n chosen_entry: A JSON respresentation of the chosen entry that will be used to\n construct the filters.\n\n Returns:\n Boolean indicating success (`True`) or failure/irrelevance\n (`None`) and the string summary of the test case.\n\n \"\"\"\n # Explicitly handle top level keys that do not have types in info\n if not chosen_entry:\n raise ResponseError(\n f\"Chosen entry of endpoint '/{endp}' failed validation.\"\n )\n\n if prop == \"type\":\n if chosen_entry.get(\"type\") == endp:\n return True, f\"Successfully validated {prop}\"\n raise ResponseError(\n f\"Chosen entry of endpoint '{endp}' had unexpected or missing type: {chosen_entry.get('type')!r}.\"\n )\n\n prop_type = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"type\")\n if prop_type is None\n else prop_type\n )\n\n if prop_type is None:\n raise ResponseError(\n f\"Cannot validate queries on {prop!r} as field type was not reported in `/info/{endp}`\"\n )\n\n # this is the case of a provider field\n if prop not in CONF.entry_schemas[endp]:\n if self.provider_prefix is None:\n raise ResponseError(\n f\"Found unknown field {prop!r} in `/info/{endp}` and no provider prefix was provided in `/info`\"\n )\n elif not prop.startswith(f\"_{self.provider_prefix}_\"):\n raise ResponseError(\n f\"Found unknown field {prop!r} that did not start with provider prefix '_{self.provider_prefix}_'\"\n )\n return (\n None,\n f\"Found provider field {prop!r}, will not test queries as they are strictly optional.\",\n )\n\n query_optional = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n == SupportLevel.OPTIONAL\n )\n\n return self._construct_single_property_filters(\n prop, prop_type, sortable, endp, chosen_entry, query_optional\n )\n\n @staticmethod\n def _format_test_value(test_value: Any, prop_type: DataType, operator: str) -> str:\n \"\"\"Formats the test value as a string according to the type of the property.\n\n Parameters:\n test_value: The value to format.\n prop_type: The OPTIMADE data type of the field.\n operator: The operator that will be applied to it.\n\n Returns:\n The value formatted as a string to use in an OPTIMADE filter.\n\n \"\"\"\n if prop_type == DataType.LIST:\n if operator in (\"HAS ALL\", \"HAS ANY\"):\n _vals = sorted(set(test_value))\n if isinstance(test_value[0], str):\n _vals = [f'\"{val}\"' for val in _vals]\n else:\n _vals = [f\"{val}\" for val in _vals]\n _test_value = \",\".join(_vals)\n elif operator == \"LENGTH\":\n _test_value = f\"{len(test_value)}\"\n else:\n if isinstance(test_value[0], str):\n _test_value = f'\"{test_value[0]}\"'\n else:\n _test_value = test_value[0]\n\n elif prop_type in (DataType.STRING, DataType.TIMESTAMP):\n _test_value = f'\"{test_value}\"'\n\n else:\n _test_value = test_value\n\n return _test_value\n\n def _construct_single_property_filters(\n self,\n prop: str,\n prop_type: DataType,\n sortable: bool,\n endp: str,\n chosen_entry: dict[str, Any],\n query_optional: bool,\n ) -> tuple[Optional[bool], str]:\n \"\"\"This method constructs appropriate queries using all operators\n for a certain field and applies some tests:\n\n - inclusive operators return compatible entries, e.g. `>=` always returns\n at least the results of `=`.\n - exclusive operators never return contradictory entries, e.g.\n `nsites=1` never returns the same entries as `nsites!=1`, modulo\n pagination.\n\n Parameters:\n prop: The property name.\n prop_type: The property type.\n sortable: Whether the implementation has indicated that the field is sortable.\n endp: The corresponding entry endpoint.\n chosen_entry: A JSON respresentation of the chosen entry that will be used to\n construct the filters.\n query_optional: Whether to treat query success as optional.\n\n Returns:\n Boolean indicating success (`True`) or failure/irrelevance\n (`None`) and the string summary of the test case.\n\n \"\"\"\n if prop == \"id\":\n test_value = chosen_entry.get(\"id\")\n else:\n test_value = chosen_entry.get(\"attributes\", {}).get(prop, \"_missing\")\n\n if test_value in (\"_missing\", None):\n support = CONF.entry_schemas[endp].get(prop, {}).get(\"support\")\n queryable = CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n submsg = \"had no value\" if test_value == \"_missing\" else \"had `None` value\"\n msg = (\n f\"Chosen entry {submsg} for {prop!r} with support level {support} and queryability {queryable}, \"\n f\"so cannot construct test queries. This field should potentially be removed from the `/info/{endp}` endpoint response.\"\n )\n # None values are allowed for OPTIONAL and SHOULD, so we can just skip\n if support in (\n SupportLevel.OPTIONAL,\n SupportLevel.SHOULD,\n ):\n self._log.info(msg)\n return None, msg\n\n # Otherwise, None values are not allowed for MUST's, and entire missing fields are not allowed\n raise ResponseError(msg)\n\n using_fallback = False\n if prop_type == DataType.LIST:\n if not test_value:\n test_value = CONF.enum_fallback_values.get(endp, {}).get(prop)\n using_fallback = True\n\n if not test_value:\n msg = f\"Not testing filters on field {prop} of type {prop_type} as no test value was found to use in filter.\"\n self._log.warning(msg)\n return None, msg\n if isinstance(test_value[0], dict) or isinstance(test_value[0], list):\n msg = f\"Not testing filters on field {prop} of type {prop_type} with nested dictionary/list test value.\"\n self._log.warning(msg)\n return None, msg\n\n # Try to infer if the test value is a float from its string representation\n # and decide whether to do inclusive/exclusive query tests\n try:\n float(test_value[0])\n msg = f\"Not testing filters on field {prop} of type {prop_type} containing float values.\"\n self._log.warning(msg)\n return None, msg\n except ValueError:\n pass\n\n if prop_type in (DataType.DICTIONARY,):\n msg = f\"Not testing queries on field {prop} of type {prop_type}.\"\n self._log.warning(msg)\n return None, msg\n\n num_data_returned = {}\n\n inclusive_operators = CONF.inclusive_operators[prop_type]\n exclusive_operators = CONF.exclusive_operators[prop_type]\n field_specific_support_overrides = CONF.field_specific_overrides.get(prop, {})\n\n for operator in inclusive_operators | exclusive_operators:\n # Need to pre-format list and string test values for the query\n _test_value = self._format_test_value(test_value, prop_type, operator)\n\n query_optional = (\n query_optional\n or operator\n in field_specific_support_overrides.get(SupportLevel.OPTIONAL, [])\n )\n\n query = f\"{prop} {operator} {_test_value}\"\n request = f\"{endp}?filter={query}\"\n response, message = self._get_endpoint(\n request,\n multistage=True,\n optional=query_optional,\n expected_status_code=(200, 501),\n )\n\n if response is None or response.status_code != 200:\n if query_optional:\n return (\n None,\n \"Optional query {query!r} raised the error: {message}.\",\n )\n raise ResponseError(\n f\"Unable to perform mandatory query {query!r}, which raised the error: {message}\"\n )\n\n response = response.json()\n\n if \"meta\" not in response or \"more_data_available\" not in response[\"meta\"]:\n raise ResponseError(\n f\"Required field `meta->more_data_available` missing from response for {request}.\"\n )\n\n if not response[\"meta\"][\"more_data_available\"] and \"data\" in response:\n num_data_returned[operator] = len(response[\"data\"])\n else:\n num_data_returned[operator] = response[\"meta\"].get(\"data_returned\")\n\n if prop in CONF.unique_properties and operator == \"=\":\n if num_data_returned[\"=\"] is not None and num_data_returned[\"=\"] == 0:\n raise ResponseError(\n f\"Unable to filter field 'id' for equality, no data was returned for {query}.\"\n )\n if num_data_returned[\"=\"] is not None and num_data_returned[\"=\"] > 1:\n raise ResponseError(\n f\"Filter for an individual 'id' returned {num_data_returned['=']} results, when only 1 was expected.\"\n )\n\n num_response = num_data_returned[operator]\n\n excluded = operator in exclusive_operators\n # if we have all results on this page, check that the blessed ID is in the response\n if excluded and (\n chosen_entry.get(\"id\", \"\")\n in {entry.get(\"id\") for entry in response[\"data\"]}\n ):\n raise ResponseError(\n f\"Entry {chosen_entry['id']} with value {prop!r}: {test_value} was not excluded by {query!r}\"\n )\n\n # check that at least the archetypal structure was returned, unless we are using a fallback value\n if not excluded and not using_fallback:\n if (\n num_data_returned[operator] is not None\n and num_data_returned[operator] < 1\n ):\n raise ResponseError(\n f\"Supposedly inclusive query {query!r} did not include original entry ID {chosen_entry['id']!r} \"\n f\"(with field {prop!r} = {test_value}) potentially indicating a problem with filtering on this field.\"\n )\n\n # check that the filter returned no entries that had a null or missing value for the filtered property\n if any(\n entry.get(\"attributes\", {}).get(prop, entry.get(prop, None)) is None\n for entry in response.get(\"data\", [])\n ):\n raise ResponseError(\n f\"Filter {query!r} on field {prop!r} returned entries that had null or missing values for the field.\"\n )\n\n # Numeric and string comparisons must work both ways...\n if prop_type in (\n DataType.STRING,\n DataType.INTEGER,\n DataType.FLOAT,\n DataType.TIMESTAMP,\n ) and operator not in (\n \"CONTAINS\",\n \"STARTS\",\n \"STARTS WITH\",\n \"ENDS\",\n \"ENDS WITH\",\n ):\n reversed_operator = operator.replace(\"<\", \">\")\n if \"<\" in operator:\n reversed_operator = operator.replace(\"<\", \">\")\n elif \">\" in operator:\n reversed_operator = operator.replace(\">\", \"<\")\n\n # Don't try to reverse string comparison as it is ill-defined\n if prop_type == DataType.STRING and any(\n comp in operator for comp in (\"<\", \">\")\n ):\n continue\n\n reversed_query = f\"{_test_value} {reversed_operator} {prop}\"\n reversed_request = f\"{endp}?filter={reversed_query}\"\n reversed_response, message = self._get_endpoint(\n reversed_request,\n multistage=True,\n optional=query_optional,\n expected_status_code=(200, 501),\n )\n\n if not reversed_response:\n if query_optional:\n return (\n None,\n \"Optional query {reversed_query!r} raised the error: {message}.\",\n )\n raise ResponseError(\n f\"Unable to perform mandatory query {reversed_query!r}, which raised the error: {message}\"\n )\n\n reversed_response = reversed_response.json()\n if (\n \"meta\" not in reversed_response\n or \"more_data_available\" not in reversed_response[\"meta\"]\n ):\n raise ResponseError(\n f\"Required field `meta->more_data_available` missing from response for {request}.\"\n )\n\n if not reversed_response[\"meta\"][\"more_data_available\"]:\n num_reversed_response = len(reversed_response[\"data\"])\n else:\n num_reversed_response = reversed_response[\"meta\"].get(\n \"data_returned\"\n )\n\n if num_response is not None and num_reversed_response is not None:\n if reversed_response[\"meta\"].get(\"data_returned\") != response[\n \"meta\"\n ].get(\"data_returned\"):\n raise ResponseError(\n f\"Query {query} did not work both ways around: {reversed_query}, \"\n \"returning a different number of results each time (as reported by `meta->data_returned`)\"\n )\n\n # check that the filter returned no entries that had a null or missing value for the filtered property\n if any(\n entry.get(\"attributes\", {}).get(prop, entry.get(prop, None)) is None\n for entry in reversed_response.get(\"data\", [])\n ):\n raise ResponseError(\n f\"Filter {reversed_query!r} on field {prop!r} returned entries that had null or missing values for the field.\"\n )\n\n return True, f\"{prop} passed filter tests\"\n\n def _test_info_or_links_endpoint(\n self, request_str: str\n ) -> Union[Literal[False], dict]:\n \"\"\"Requests an info or links endpoint and attempts to deserialize\n the response.\n\n Parameters:\n request_str: The request to make, e.g. \"links\".\n\n Returns:\n `False` if the info response failed deserialization,\n otherwise returns the deserialized object.\n\n \"\"\"\n response, _ = self._get_endpoint(request_str)\n if response:\n deserialized, _ = self._deserialize_response(\n response,\n self._response_classes[request_str],\n request=request_str,\n )\n if deserialized:\n return deserialized.model_dump()\n\n return False\n\n def _test_single_entry_endpoint(self, endp: str) -> None:\n \"\"\"Requests and deserializes a single entry endpoint with the\n appropriate model.\n\n Parameters:\n request_str: The single entry request to make, e.g. \"structures/id_1\".\n\n \"\"\"\n response_cls_name = endp + \"/\"\n if response_cls_name in self._response_classes:\n response_cls = self._response_classes[response_cls_name]\n else:\n self._log.warning(\n \"Deserializing single entry response %s with generic response rather than defined endpoint.\",\n endp,\n )\n response_cls = ValidatorEntryResponseOne\n\n response_fields = set()\n if endp in CONF.entry_schemas:\n response_fields = (\n set(CONF.entry_schemas[endp].keys())\n - CONF.top_level_non_attribute_fields\n )\n\n if endp in self._test_id_by_type:\n test_id = self._test_id_by_type[endp]\n request_str = f\"{endp}/{test_id}\"\n if response_fields:\n request_str += f\"?response_fields={','.join(response_fields)}\"\n response, _ = self._get_endpoint(request_str)\n if response:\n self._test_meta_schema_reporting(response, request_str, optional=True)\n self._deserialize_response(response, response_cls, request=request_str)\n\n def _test_multi_entry_endpoint(self, endp: str) -> None:\n \"\"\"Requests and deserializes a multi-entry endpoint with the\n appropriate model.\n\n Parameters:\n request_str: The multi-entry request to make, e.g.,\n \"structures?filter=nsites<10\"\n\n \"\"\"\n if endp in self._response_classes:\n response_cls = self._response_classes[endp]\n else:\n self._log.warning(\n \"Deserializing multi entry response from %s with generic response rather than defined endpoint.\",\n endp,\n )\n response_cls = ValidatorEntryResponseMany\n\n response_fields = set()\n if endp in CONF.entry_schemas:\n response_fields = (\n set(CONF.entry_schemas[endp].keys())\n - CONF.top_level_non_attribute_fields\n )\n\n request_str = f\"{endp}?page_limit={self.page_limit}\"\n\n if response_fields:\n request_str += f'&response_fields={\",\".join(response_fields)}'\n\n response, _ = self._get_endpoint(request_str)\n\n self._test_if_data_empty(response, request_str, optional=True)\n self._test_meta_schema_reporting(response, request_str, optional=True)\n self._test_page_limit(response)\n\n deserialized, _ = self._deserialize_response(\n response, response_cls, request=request_str\n )\n\n self._get_single_id_from_multi_entry_endpoint(deserialized, request=request_str)\n if deserialized:\n self._test_data_available_matches_data_returned(\n deserialized, request=request_str\n )\n\n @test_case\n def _test_data_available_matches_data_returned(\n self, deserialized: Any\n ) -> tuple[Optional[bool], str]:\n \"\"\"In the case where no query is requested, `data_available`\n must equal `data_returned` in the meta response, which is tested\n here.\n\n Parameters:\n deserialized: The deserialized response to a multi-entry\n endpoint.\n\n Returns:\n `True` if successful, with a string summary.\n\n \"\"\"\n if (\n deserialized.meta.data_available is None\n or deserialized.meta.data_returned is None\n ):\n return (\n None,\n \"`meta->data_available` and/or `meta->data_returned` were not provided.\",\n )\n\n if deserialized.meta.data_available != deserialized.meta.data_returned:\n raise ResponseError(\n f\"No query was performed, but `data_returned` != `data_available` {deserialized.meta.data_returned} vs {deserialized.meta.data_available}.\"\n )\n\n return (\n True,\n \"Meta response contained correct values for data_available and data_returned.\",\n )\n\n def _test_versions_endpoint(self):\n \"\"\"Requests and validate responses for the versions endpoint,\n which MUST exist for unversioned base URLs and MUST NOT exist\n for versioned base URLs.\n\n \"\"\"\n\n # First, check that there is a versions endpoint in the appropriate place:\n # If passed a versioned URL, then strip that version from\n # the URL before looking for `/versions`.\n _old_base_url = self.base_url\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n self.client.base_url = \"/\".join(self.client.base_url.split(\"/\")[:-1])\n self.base_url = self.client.base_url\n\n response, _ = self._get_endpoint(\n CONF.versions_endpoint, expected_status_code=200\n )\n\n if response:\n self._test_versions_endpoint_content(\n response, request=CONF.versions_endpoint\n )\n\n # If passed a versioned URL, first reset the URL of the client to the\n # versioned one, then test that this versioned URL does NOT host a versions endpoint\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n self.client.base_url = _old_base_url\n self.base_url = _old_base_url\n self._get_endpoint(CONF.versions_endpoint, expected_status_code=404)\n\n @test_case\n def _test_versions_endpoint_content(\n self, response: requests.Response\n ) -> tuple[requests.Response, str]:\n \"\"\"Checks that the response from the versions endpoint complies\n with the specification and that its 'Content-Type' header complies with\n [RFC 4180](https://tools.ietf.org/html/rfc4180.html).\n\n Parameters:\n response: The HTTP response from the versions endpoint.\n\n Raises:\n ResponseError: If any content checks fail.\n\n Returns:\n The successful HTTP response or `None`, and a summary string.\n\n \"\"\"\n text_content = response.text.strip().split(\"\\n\")\n if text_content[0] != \"version\":\n raise ResponseError(\n f\"First line of `/{CONF.versions_endpoint}` response must be 'version', not {text_content[0]!r}\"\n )\n\n if len(text_content) <= 1:\n raise ResponseError(\n f\"No version numbers found in `/{CONF.versions_endpoint}` response, only {text_content}\"\n )\n\n for version in text_content[1:]:\n try:\n int(version)\n except ValueError:\n raise ResponseError(\n f\"Version numbers reported by `/{CONF.versions_endpoint}` must be integers specifying the major version, not {text_content}.\"\n )\n\n _content_type = response.headers.get(\"content-type\")\n if not _content_type:\n raise ResponseError(\n \"Missing 'Content-Type' in response header from `/versions`.\"\n )\n\n content_type = [_.replace(\" \", \"\") for _ in _content_type.split(\";\")]\n\n self._test_versions_headers(\n content_type,\n (\"text/csv\", \"text/plain\"),\n optional=True,\n request=CONF.versions_endpoint,\n )\n self._test_versions_headers(\n content_type,\n \"header=present\",\n optional=True,\n request=CONF.versions_endpoint,\n )\n\n return response, \"`/versions` endpoint responded correctly.\"\n\n @test_case\n def _test_versions_headers(\n self,\n content_type: dict[str, Any],\n expected_parameter: Union[str, list[str]],\n ) -> tuple[dict[str, Any], str]:\n \"\"\"Tests that the `Content-Type` field of the `/versions` header contains\n the passed parameter.\n\n Arguments:\n content_type: The 'Content-Type' field from the response of the `/versions` endpoint.\n expected_paramter: A substring or list of substrings that are expected in\n the Content-Type of the response. If multiple strings are passed, they will\n be treated as possible alternatives to one another.\n\n Raises:\n ResponseError: If the expected 'Content-Type' parameter is missing.\n\n Returns:\n The HTTP response headers and a summary string.\n\n \"\"\"\n\n if isinstance(expected_parameter, str):\n expected_parameter = [expected_parameter]\n\n if not any(param in content_type for param in expected_parameter):\n raise ResponseError(\n f\"Incorrect 'Content-Type' header {';'.join(content_type)!r}.\\n\"\n f\"Missing at least one expected parameter(s): {expected_parameter!r}\"\n )\n\n return (\n content_type,\n f\"`/versions` response had one of the expected Content-Type parameters {expected_parameter}.\",\n )\n\n def _test_as_type(self) -> None:\n \"\"\"Tests that the base URL of the validator (i.e. with no\n additional path added) validates with the model selected.\n\n \"\"\"\n response, _ = self._get_endpoint(\"\")\n if response:\n self._log.debug(\"Deserialzing response as type %s\", self.as_type_cls)\n self._deserialize_response(response, self.as_type_cls)\n\n def _test_bad_version_returns_553(self) -> None:\n \"\"\"Tests that a garbage version number responds with a 553\n error code.\n\n \"\"\"\n expected_status_code = [553]\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n expected_status_code = [404, 400]\n\n self._get_endpoint(\n \"v123123/info\", expected_status_code=expected_status_code, optional=True\n )\n\n @test_case\n def _test_if_data_empty(\n self,\n response: requests.models.Response,\n request_str: str,\n ):\n \"\"\"Tests whether an endpoint responds a entries under `data`.\"\"\"\n try:\n if not response.json().get(\"data\", []):\n raise ResponseError(\n f\"Query {request_str} did not respond with any entries under `data`. This may not consitute an error, but should be checked.\"\n )\n\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to test presence of `data` for query {request_str}: could not decode response as JSON.\\n{str(response.content)}\"\n )\n\n return (\n True,\n f\"Query {request_str} successfully returned some entries.\",\n )\n\n @test_case\n def _test_meta_schema_reporting(\n self,\n response: requests.models.Response,\n request_str: str,\n ):\n \"\"\"Tests that the endpoint responds with a `meta->schema`.\"\"\"\n try:\n if not response.json().get(\"meta\", {}).get(\"schema\"):\n raise ResponseError(\n f\"Query {request_str} did not report a schema in `meta->schema` field.\"\n )\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to test presence of `meta->schema`: could not decode response as JSON.\\n{str(response.content)}\"\n )\n\n return (\n True,\n f\"Query {request_str} successfully reported a schema in `meta->schema`.\",\n )\n\n @test_case\n def _test_page_limit(\n self,\n response: requests.models.Response,\n check_next_link: int = 5,\n previous_links: Optional[set[str]] = None,\n ) -> tuple[Optional[bool], str]:\n \"\"\"Test that a multi-entry endpoint obeys the page limit by\n following pagination links up to a depth of `check_next_link`.\n\n Parameters:\n response: The response to test for page limit\n compliance.\n check_next_link: Maximum recursion depth for following\n pagination links.\n previous_links: A set of previous links that will be used\n to check that the `next` link is actually new.\n\n Returns:\n `True` if the test was successful and `None` if not, with a\n string summary.\n\n \"\"\"\n if previous_links is None:\n previous_links = set()\n try:\n response_json = response.json()\n except (AttributeError, json.JSONDecodeError):\n raise ResponseError(\"Unable to test endpoint `page_limit` parameter.\")\n\n try:\n num_entries = len(response_json[\"data\"])\n except (KeyError, TypeError):\n raise ResponseError(\n \"Response under `data` field was missing or had wrong type.\"\n )\n\n if num_entries > self.page_limit:\n raise ResponseError(\n f\"Endpoint did not obey page limit: {num_entries} entries vs {self.page_limit} limit\"\n )\n\n try:\n more_data_available = response_json[\"meta\"][\"more_data_available\"]\n except KeyError:\n raise ResponseError(\"Field `meta->more_data_available` was missing.\")\n\n if more_data_available and check_next_link:\n try:\n next_link = response_json[\"links\"][\"next\"]\n if isinstance(next_link, dict):\n next_link = next_link[\"href\"]\n if not next_link:\n raise ResponseError(\n \"Endpoint suggested more data was available but provided no valid links->next link.\"\n )\n except KeyError:\n raise ResponseError(\n \"Endpoint suggested more data was available but provided no valid links->next link.\"\n )\n\n if next_link in previous_links:\n raise ResponseError(\n f\"The next link {next_link} has been provided already for a previous page.\"\n )\n previous_links.add(next_link)\n\n if not isinstance(next_link, str):\n raise ResponseError(\n f\"Unable to parse links->next {next_link!r} as a link.\"\n )\n\n self._log.debug(\"Following pagination link to %r.\", next_link)\n next_response, _ = self._get_endpoint(next_link)\n if not next_response:\n raise ResponseError(\n f\"Error when testing pagination: the response from `links->next` {next_link!r} failed the previous test.\"\n )\n\n check_next_link = check_next_link - 1\n self._test_page_limit(\n next_response,\n check_next_link=check_next_link,\n multistage=bool(check_next_link),\n previous_links=previous_links,\n )\n\n return (\n True,\n f\"Endpoint obeyed page limit of {self.page_limit} by returning {num_entries} entries.\",\n )\n\n @test_case\n def _get_single_id_from_multi_entry_endpoint(self, deserialized):\n \"\"\"Scrape an ID from the multi-entry endpoint to use as query\n for single entry endpoint.\n\n \"\"\"\n if deserialized and deserialized.data:\n self._test_id_by_type[deserialized.data[0].type] = deserialized.data[0].id\n self._log.debug(\n \"Set type %s test ID to %s\",\n deserialized.data[0].type,\n deserialized.data[0].id,\n )\n else:\n return (\n None,\n \"No entries found under endpoint to scrape ID from. \"\n \"This may be caused by previous errors, if e.g. the endpoint failed deserialization.\",\n )\n return (\n self._test_id_by_type[deserialized.data[0].type],\n f\"successfully scraped test ID from {deserialized.data[0].type} endpoint\",\n )\n\n @test_case\n def _deserialize_response(\n self,\n response: requests.models.Response,\n response_cls: Any,\n request: Optional[str] = None,\n ) -> tuple[Any, str]:\n \"\"\"Try to create the appropriate pydantic model from the response.\n\n Parameters:\n response: The response to try to deserialize.\n response_cls: The class to use for deserialization.\n request: Optional string that will be displayed as the attempted\n request in the validator output.\n\n Returns:\n The deserialized object (or `None` if unsuccessful) and a\n human-readable summary\n\n \"\"\"\n if not response:\n raise ResponseError(\"Request failed\")\n try:\n json_response = response.json()\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to decode response as JSON. Response: {response}\"\n )\n\n self._log.debug(\n f\"Deserializing {json.dumps(json_response, indent=2)} as model {response_cls}\"\n )\n\n return (\n response_cls(**json_response),\n f\"deserialized correctly as object of type {response_cls}\",\n )\n\n @test_case\n def _get_available_endpoints(\n self, base_info: Union[Any, dict[str, Any]]\n ) -> tuple[Optional[list[str]], str]:\n \"\"\"Tries to get `entry_types_by_format` from base info response\n even if it could not be deserialized.\n\n Parameters:\n base_info: Either the unvalidated JSON representation of the\n base info, or the deserialized object.\n\n Returns:\n The list of JSON entry endpoints (or `None` if unavailable)\n and a string summary.\n\n \"\"\"\n available_json_entry_endpoints = []\n for _ in [0]:\n try:\n available_json_entry_endpoints = base_info[\"data\"][\"attributes\"][\n \"entry_types_by_format\"\n ][\"json\"]\n break\n except (KeyError, TypeError):\n raise ResponseError(\n \"Unable to get entry_types_by_format from unserializable base info response {}.\".format(\n base_info\n )\n )\n else:\n raise ResponseError(\n \"Unable to find any JSON entry types in entry_types_by_format\"\n )\n\n if self.index and available_json_entry_endpoints != []:\n raise ResponseError(\n \"No entry endpoint are allowed for an Index meta-database\"\n )\n\n for non_entry_endpoint in CONF.non_entry_endpoints:\n if non_entry_endpoint in available_json_entry_endpoints:\n raise ResponseError(\n f'Illegal entry \"{non_entry_endpoint}\" was found in entry_types_by_format\"'\n )\n\n # Filter out custom extension endpoints that are not covered in the specification\n available_json_entry_endpoints = [\n endp\n for endp in available_json_entry_endpoints\n if endp in CONF.entry_endpoints\n ]\n\n return (\n available_json_entry_endpoints,\n \"successfully found available entry types in baseinfo\",\n )\n\n @test_case\n def _get_endpoint(\n self, request_str: str, expected_status_code: Union[list[int], int] = 200\n ) -> tuple[Optional[requests.Response], str]:\n \"\"\"Gets the response from the endpoint specified by `request_str`.\n function is wrapped by the `test_case` decorator\n\n Parameters:\n request_str: The request to make to the client.\n expected_status_code: If the request responds with a different\n status code to this one, raise a ResponseError.\n\n Returns:\n The response to the request (if successful) or `None`, plus\n a string summary.\n\n \"\"\"\n request_str = request_str.replace(\"\\n\", \"\")\n response = self.client.get(request_str)\n\n if isinstance(expected_status_code, int):\n expected_status_code = [expected_status_code]\n\n message = f\"received expected response: {response}.\"\n\n if response.status_code != 200:\n message = f\"Request to '{request_str}' returned HTTP status code {response.status_code}.\"\n message += \"\\nAdditional details from implementation:\"\n try:\n for error in response.json().get(\"errors\", []):\n message += f'\\n {error.get(\"title\", \"N/A\")}: {error.get(\"detail\", \"N/A\")} ({error.get(\"source\", {}).get(\"pointer\", \"N/A\")})'\n except json.JSONDecodeError:\n message += f\"\\n Could not parse response as JSON. Content type was {response.headers.get('content-type')!r}.\"\n\n if response.status_code not in expected_status_code:\n raise ResponseError(message)\n\n return response, message\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.__init__","title":"__init__(client=None, base_url=None, verbosity=0, respond_json=False, page_limit=4, max_retries=5, run_optional_tests=True, fail_fast=False, as_type=None, index=False, minimal=False, http_headers=None, timeout=DEFAULT_CONN_TIMEOUT, read_timeout=DEFAULT_READ_TIMEOUT)
","text":"Set up the tests to run, based on constants in this module for required endpoints.
Parameters:
Name Type Description Defaultclient
Optional[Any]
A client that has a .get()
method to obtain the response from the implementation. If None
, then Client
will be used.
None
base_url
Optional[str]
The URL of the implementation to validate. Unless performing \"as_type\" validation, this should point to the base of the OPTIMADE implementation.
None
verbosity
int
The verbosity of the output and logging as an integer (0
: critical, 1
: warning, 2
: info, 3
: debug).
0
respond_json
bool
If True
, print only a JSON representation of the results of validation to stdout.
False
page_limit
int
The default page limit to apply to filters.
4
max_retries
int
Argument is passed to the client for how many attempts to make for a request before failing.
5
run_optional_tests
bool
Whether to run the tests on optional OPTIMADE features.
True
fail_fast
bool
Whether to exit validation after the first failure of a mandatory test.
False
as_type
Optional[str]
An OPTIMADE entry or endpoint type to coerce the response from implementation into, e.g. \"structures\". Requires base_url
to be pointed to the corresponding endpoint.
None
index
bool
Whether to validate the implementation as an index meta-database.
False
minimal
bool
Whether or not to run only a minimal test set.
False
http_headers
Optional[dict[str, str]]
Dictionary of additional headers to add to every request.
None
timeout
float
The connection timeout to use for all requests (in seconds).
DEFAULT_CONN_TIMEOUT
read_timeout
float
The read timeout to use for all requests (in seconds).
DEFAULT_READ_TIMEOUT
Source code in optimade/validator/validator.py
def __init__(\n self,\n client: Optional[Any] = None,\n base_url: Optional[str] = None,\n verbosity: int = 0,\n respond_json: bool = False,\n page_limit: int = 4,\n max_retries: int = 5,\n run_optional_tests: bool = True,\n fail_fast: bool = False,\n as_type: Optional[str] = None,\n index: bool = False,\n minimal: bool = False,\n http_headers: Optional[dict[str, str]] = None,\n timeout: float = DEFAULT_CONN_TIMEOUT,\n read_timeout: float = DEFAULT_READ_TIMEOUT,\n):\n \"\"\"Set up the tests to run, based on constants in this module\n for required endpoints.\n\n Arguments:\n client: A client that has a `.get()` method to obtain the\n response from the implementation. If `None`, then\n [`Client`][optimade.validator.utils.Client] will be used.\n base_url: The URL of the implementation to validate. Unless\n performing \"as_type\" validation, this should point to the\n base of the OPTIMADE implementation.\n verbosity: The verbosity of the output and logging as an integer\n (`0`: critical, `1`: warning, `2`: info, `3`: debug).\n respond_json: If `True`, print only a JSON representation of the\n results of validation to stdout.\n page_limit: The default page limit to apply to filters.\n max_retries: Argument is passed to the client for how many\n attempts to make for a request before failing.\n run_optional_tests: Whether to run the tests on optional\n OPTIMADE features.\n fail_fast: Whether to exit validation after the first failure\n of a mandatory test.\n as_type: An OPTIMADE entry or endpoint type to coerce the response\n from implementation into, e.g. \"structures\". Requires `base_url`\n to be pointed to the corresponding endpoint.\n index: Whether to validate the implementation as an index meta-database.\n minimal: Whether or not to run only a minimal test set.\n http_headers: Dictionary of additional headers to add to every request.\n timeout: The connection timeout to use for all requests (in seconds).\n read_timeout: The read timeout to use for all requests (in seconds).\n\n \"\"\"\n self.verbosity = verbosity\n self.max_retries = max_retries\n self.page_limit = page_limit\n self.index = index\n self.run_optional_tests = run_optional_tests\n self.fail_fast = fail_fast\n self.respond_json = respond_json\n self.minimal = minimal\n\n if as_type is None:\n self.as_type_cls = None\n elif self.index:\n if as_type not in CONF.response_classes_index:\n raise RuntimeError(\n f\"Provided as_type='{as_type}' not allowed for an Index meta-database.\"\n )\n self.as_type_cls = CONF.response_classes_index[as_type]\n elif as_type in (\"structure\", \"reference\"):\n self.as_type_cls = CONF.response_classes[f\"{as_type}s/\"]\n else:\n self.as_type_cls = CONF.response_classes[as_type]\n\n if client is None and base_url is None:\n raise RuntimeError(\n \"Need at least a URL or a client to initialize validator.\"\n )\n if base_url and client:\n raise RuntimeError(\"Please specify at most one of base_url or client.\")\n if client:\n self.client = client\n self.base_url = self.client.base_url\n # If a custom client has been provided, try to set custom headers if they have been specified,\n # but do not overwrite any existing attributes\n if http_headers:\n if not hasattr(self.client, \"headers\"):\n self.client.headers = http_headers\n else:\n print_warning(\n f\"Not using specified request headers {http_headers} with custom client {self.client}.\"\n )\n else:\n while base_url.endswith(\"/\"): # type: ignore[union-attr]\n base_url = base_url[:-1] # type: ignore[index]\n self.base_url = base_url\n self.client = Client(\n self.base_url, # type: ignore[arg-type]\n max_retries=self.max_retries,\n headers=http_headers,\n timeout=timeout,\n read_timeout=read_timeout,\n )\n\n self._setup_log()\n\n self._response_classes = (\n CONF.response_classes_index if self.index else CONF.response_classes\n )\n\n # some simple checks on base_url\n self.base_url = str(self.base_url)\n self.base_url_parsed = urllib.parse.urlparse(self.base_url)\n # only allow filters/endpoints if we are working in \"as_type\" mode\n if self.as_type_cls is None and self.base_url_parsed.query:\n raise SystemExit(\n f\"Base URL {self.base_url} not appropriate: should not contain a filter.\"\n )\n\n self.valid = None\n\n self._test_id_by_type: dict[str, Any] = {}\n self._entry_info_by_type: dict[str, Any] = {}\n\n self.results = ValidatorResults(verbosity=self.verbosity)\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.print_summary","title":"print_summary()
","text":"Print a summary of the results of validation.
Source code inoptimade/validator/validator.py
def print_summary(self):\n \"\"\"Print a summary of the results of validation.\"\"\"\n if self.respond_json:\n print(json.dumps(dataclasses.asdict(self.results), indent=2))\n return\n\n if self.results.failure_messages:\n print(\"\\n\\nFAILURES\")\n print(\"========\\n\")\n for message in self.results.failure_messages:\n print_failure(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.optional_failure_messages:\n print(\"\\n\\nOPTIONAL TEST FAILURES\")\n print(\"======================\\n\")\n for message in self.results.optional_failure_messages:\n print_notify(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.internal_failure_messages:\n print(\"\\n\\nINTERNAL FAILURES\")\n print(\"=================\\n\")\n print(\n \"There were internal validator failures associated with this run.\\n\"\n \"If this problem persists, please report it at:\\n\"\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues/new\\n\"\n )\n\n for message in self.results.internal_failure_messages:\n print_warning(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.valid or (not self.valid and not self.fail_fast):\n final_message = f\"\\n\\nPassed {self.results.success_count} out of {self.results.success_count + self.results.failure_count + self.results.internal_failure_count} tests.\"\n if not self.valid:\n print_failure(final_message)\n else:\n print_success(final_message)\n\n if self.run_optional_tests and not self.fail_fast:\n print(\n f\"Additionally passed {self.results.optional_success_count} out of \"\n f\"{self.results.optional_success_count + self.results.optional_failure_count} optional tests.\"\n )\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.validate_implementation","title":"validate_implementation()
","text":"Run all the test cases on the implementation, or the single type test, depending on what options were provided on initialiation.
Sets the self.valid
attribute to True
or False
depending on the outcome of the tests.
Raises:
Type DescriptionRuntimeError
If it was not possible to start the validation process.
Source code inoptimade/validator/validator.py
def validate_implementation(self):\n \"\"\"Run all the test cases on the implementation, or the single type test,\n depending on what options were provided on initialiation.\n\n Sets the `self.valid` attribute to `True` or `False` depending on the\n outcome of the tests.\n\n Raises:\n RuntimeError: If it was not possible to start the validation process.\n\n \"\"\"\n # If a single \"as type\" has been set, only run that test\n if self.as_type_cls is not None:\n self._log.debug(\n \"Validating response of %s with model %s\",\n self.base_url,\n self.as_type_cls,\n )\n self._test_as_type()\n self.valid = not bool(self.results.failure_count)\n self.print_summary()\n return\n\n # Test entire implementation\n if self.verbosity >= 0:\n print(f\"Testing entire implementation at {self.base_url}\")\n info_endp = CONF.info_endpoint\n self._log.debug(\"Testing base info endpoint of %s\", info_endp)\n\n # Get and validate base info to find endpoints\n # If this is not possible, then exit at this stage\n base_info = self._test_info_or_links_endpoint(info_endp)\n if not base_info:\n self._log.critical(\n f\"Unable to deserialize response from introspective {info_endp!r} endpoint. \"\n \"This is required for all further validation, so the validator will now exit.\"\n )\n # Set valid to False to ensure error code 1 is raised at CLI\n self.valid = False\n self.print_summary()\n return\n\n # Grab the provider prefix from base info and use it when looking for provider fields\n self.provider_prefix = None\n meta = base_info.get(\"meta\", {})\n if meta.get(\"provider\") is not None:\n self.provider_prefix = meta[\"provider\"].get(\"prefix\")\n\n # Set the response class for all `/info/entry` endpoints based on `/info` response\n self.available_json_endpoints, _ = self._get_available_endpoints(\n base_info, request=info_endp\n )\n for endp in self.available_json_endpoints:\n self._response_classes[f\"{info_endp}/{endp}\"] = EntryInfoResponse\n\n # Run some tests on the versions endpoint\n self._log.debug(\"Testing versions endpoint %s\", CONF.versions_endpoint)\n self._test_versions_endpoint()\n self._test_bad_version_returns_553()\n\n # Test that entry info endpoints deserialize correctly\n # If they do not, the corresponding entry in _entry_info_by_type\n # is set to False, which must be checked for further validation\n for endp in self.available_json_endpoints:\n entry_info_endpoint = f\"{info_endp}/{endp}\"\n self._log.debug(\"Testing expected info endpoint %s\", entry_info_endpoint)\n self._entry_info_by_type[endp] = self._test_info_or_links_endpoint(\n entry_info_endpoint\n )\n\n # Test that the results from multi-entry-endpoints obey, e.g. page limits,\n # and that all entries can be deserialized with the patched models.\n # These methods also set the test_ids for each type of entry, which are validated\n # in the next loop.\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing multiple entry endpoint of %s\", endp)\n self._test_multi_entry_endpoint(endp)\n\n # Test that the single IDs scraped earlier work with the single entry endpoint\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing single entry request of type %s\", endp)\n self._test_single_entry_endpoint(endp)\n\n # Use the _entry_info_by_type to construct filters on the relevant endpoints\n if not self.minimal:\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing queries on JSON entry endpoint of %s\", endp)\n self._recurse_through_endpoint(endp)\n\n # Test that the links endpoint can be serialized correctly\n self._log.debug(\"Testing %s endpoint\", CONF.links_endpoint)\n self._test_info_or_links_endpoint(CONF.links_endpoint)\n\n self.valid = not (\n self.results.failure_count or self.results.internal_failure_count\n )\n\n self.print_summary()\n
"},{"location":"concepts/filtering/","title":"Filter parsing and transforming","text":"One of the aims of this package is to integrate with existing databases and APIs, and as such your particular backend may not have a supported filter transformer. This guide will briefly outline how to parse OPTIMADE filter strings into database or API-specific queries.
"},{"location":"concepts/filtering/#parsing-optimade-filter-strings","title":"Parsing OPTIMADE filter strings","text":"The LarkParser
class will take an OPTIMADE filter string, supplied by the user, and parse it into a lark.Tree
instance.
Example use:
from optimade.filterparser import LarkParser\n\np = LarkParser(version=(1, 0, 0))\ntree = p.parse(\"nelements<3\")\nprint(tree)\n
Tree('filter', [Tree('expression', [Tree('expression_clause', [Tree('expression_phrase', [Tree('comparison', [Tree('property_first_comparison', [Tree('property', [Token('IDENTIFIER', 'nelements')]), Tree('value_op_rhs', [Token('OPERATOR', '<'), Tree('value', [Tree('number', [Token('SIGNED_INT', '3')])])])])])])])])])\n
print(tree.pretty())\n
filter\n expression\n expression_clause\n expression_phrase\n comparison\n property_first_comparison\n property nelements\n value_op_rhs\n <\n value\n number 3\n
tree = p.parse('_mp_bandgap > 5.0 AND _cod_molecular_weight < 350')\nprint(tree.pretty())\n
filter\n expression\n expression_clause\n expression_phrase\n comparison\n property_first_comparison\n property _mp_bandgap\n value_op_rhs\n >\n value\n number 5.0\n expression_phrase\n comparison\n property_first_comparison\n property _cod_molecular_weight\n value_op_rhs\n <\n value\n number 350\n
"},{"location":"concepts/filtering/#flow-for-parsing-a-user-supplied-filter-and-converting-to-a-backend-query","title":"Flow for parsing a user-supplied filter and converting to a backend query","text":"After the LarkParser
has turned the filter string into a lark.Tree
, it is fed to a lark.Transformer
instance, which transforms the 'lark.Tree' into a backend-specific representation of the query. For example, MongoTransformer
will turn the tree into something useful for a MongoDB backend:
# Example: Converting to MongoDB Query Syntax\nfrom optimade.filtertransformers.mongo import MongoTransformer\n\ntransformer = MongoTransformer()\n\ntree = p.parse('_mp_bandgap > 5.0 AND _cod_molecular_weight < 350')\nquery = transformer.transform(tree)\nprint(query)\n
{\n \"$and\": [\n {\"_mp_bandgap\": {\"$gt\": 5.0}},\n {\"_cod_molecular_weight\": {\"$lt\": 350.0}}\n ]\n}\n
"},{"location":"concepts/filtering/#developing-new-filter-transformers","title":"Developing new filter transformers","text":"In order to support a new backend, you will need to create a new filter transformer that inherits from the BaseTransformer
. This transformer will need to override the methods that match the particular grammatical constructs in the Lark grammar in order to construct a query. Two examples can be found within optimade-python-tools
, one for MongoDB (MongoTransformer
) and one for Elasticsearch (ElasticTransformer
).
In some cases, you may also need to extend the base EntryCollection
, the class that receives the transformed filter as an argument to its private ._run_db_query()
method. This class handles the connections to the underlying database, formatting of the response in an OPTIMADE format, and other API features such as sorting and pagination. Again, the examples for MongoDB (MongoCollection
) and Elasticsearch (ElasticCollection
) should be helpful.
If you would like to contribute your new filter transformer back to the package, please raise an issue to signal your intent (in case someone else is already working on this). Adding a transformer requires the following:
.py
file) in the optimade/filtertransformers
folder containing an implementation of the transformer object that extends optimade.filtertransformers.base_transformer.BaseTransformer
.extra_requires
\" entry in setup.py
and in the requirements.txt
file.optimade/filtertransformers/tests
that are skipped if the required packages fail to import.optimade-python-tools
contains tools for validating external OPTIMADE implementations that may be helpful for all OPTIMADE providers. The validator is dynamic and fuzzy, in that the tested filters are generated based on random entries served by the API, and the description of the API provided at the /info
endpoint.
The validator is implemented in the optimade.validator
submodule, but the two main entry points are:
optimade-validator
script, which is installed alongside the package.optimade-validator-action
which allows the validator to be used as a GitHub Action.To run the script, simply provide an OPTIMADE URL to the script at the command-line. You can use the following to validate the Fly deployment of our reference server:
$ optimade-validator https://optimade.fly.dev/\nTesting entire implementation at https://optimade.fly.dev\n...\n
Several additional options can be found under the --help
flag, with the most important being -v/-vvvv
to set the verbosity, --index
to validate OPTIMADE index meta-databases and --json
to receive the validation results as JSON document for programmatic use.
$ optimade-validator --help\nusage: optimade-validator [-h] [-v] [-j] [-t AS_TYPE] [--index]\n [--skip-optional] [--fail-fast] [-m]\n [--page_limit PAGE_LIMIT]\n [--headers HEADERS]\n [base_url]\n\nTests OPTIMADE implementations for compliance with the optimade-python-tools models.\n\n- To test an entire implementation (at say example.com/optimade/v1) for all required/available endpoints:\n\n $ optimade-validator http://example.com/optimade/v1\n\n- To test a particular response of an implementation against a particular model:\n\n $ optimade-validator http://example.com/optimade/v1/structures/id=1234 --as-type structure\n\n- To test a particular response of an implementation against a particular model:\n\n $ optimade-validator http://example.com/optimade/v1/structures --as-type structures\n\n\npositional arguments:\n base_url The base URL of the OPTIMADE\n implementation to point at, e.g.\n 'http://example.com/optimade/v1' or\n 'http://localhost:5000/v1'\n\noptional arguments:\n -h, --help show this help message and exit\n -v, --verbosity Increase the verbosity of the output.\n (-v: warning, -vv: info, -vvv: debug)\n -j, --json Only a JSON summary of the validator\n results will be printed to stdout.\n -t AS_TYPE, --as-type AS_TYPE\n Validate the request URL with the\n provided type, rather than scanning the\n entire implementation e.g. optimade-\n validator `http://example.com/optimade/v1\n /structures/0 --as-type structure`\n --index Flag for whether the specified OPTIMADE\n implementation is an Index meta-database\n or not.\n --skip-optional Flag for whether the skip the tests of\n optional features.\n --fail-fast Whether to exit on first test failure.\n -m, --minimal Run only a minimal test set.\n --page_limit PAGE_LIMIT\n Alter the requested page limit for some\n tests.\n --headers HEADERS Additional HTTP headers to use for each\n request, specified as a JSON object.\n
"},{"location":"deployment/container/","title":"Run in a container (Docker)","text":""},{"location":"deployment/container/#retrieve-the-container-image","title":"Retrieve the container image","text":"Retrieving the container image is explained in the installation instructions. In short, using Docker you can run:
docker pull ghcr.io/materials-consortia/optimade\n
A general overview on running a container is also given in the installations instructions.
"},{"location":"deployment/container/#prepare-the-container-and-configure-the-server","title":"Prepare the container and configure the server","text":"Tip
A more complete overview of configuring the OPTIMADE server can be seen in the configuration section.
By default, the server will use the test configuration, including test data for structures and references and an in-memory mongomock database backend. This can be changed in several different ways.
One is to git clone
the repository locally and bind the repository folder to the /app
folder in the container:
# volume will bind the local version of `optimade-python-tools` to the container.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --volume /path/to/optimade-python-tools:/app \\\n ghcr.io/materials-consortia/optimade:latest\n
Help
To clone the repository you can run:
git clone --recursive https://github.com/Materials-Consortia/optimade-python-tools.git\n
You should change the /path/to/optimade-python-tools
to the full local path to the repository - or use $PWD
if you are running this command from the root of the cloned repository on a Unix system. Equivalently, %cd%
should work on Windows.
In this setup you can change the repository root file optimade_config.json
with the appropriate information. E.g., if you do not wish to use the test data you can add the insert_test_data
key with a value of false
.
Another option is to not git clone
the repository locally, but instead only generate a JSON or YAML file somewhere, where the directory of its location is bound to another location in the container that is not used, e.g., /config
or similar. As an example, let us say we generate config.yml
locally, with the full path /home/user/optimade/config.yml
. Then we can start the server with the following command:
# volume will bind the local directory to an unused folder in the container.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --volume /home/user/optimade:/config \\\n --env OPTIMADE_CONFIG_FILE=/config/config.yml \\\n ghcr.io/materials-consortia/optimade:latest\n
As shown, it is necessary to update the environment variable OPTIMADE_CONFIG_FILE
within the container to point to the new internal path to the config file. By default, this environment variable points to /app/optimade_config.json
.
This also reveals another way of configuring the server: set environment variables when running the container to supply values that would otherwise be supplied from the configuration file.
The docker run
command even has the opportunity to pass a path to a file containing a list of environment variables (--env-file /path/to/env_file
), if you wish to configure the server in this way instead of through the standard configuration file.
In this example we want to setup various services through containers that interact with each other on a closed internal network, only the OPTIMADE servers are exposed to the \"outside\".
For this example we will use Docker only. One could choose to use the Docker Compose framework to neatly express the services in a single YAML file, however, to keep this compatible with Docker alternatives, we will focus on only using Docker.
We will also setup an index meta-database, with an associated OPTIMADE server, serving data from a MongoDB backend. Hence, we need to run three separate services, configure it properly, and make sure only the OPTIMADE APIs are exposed.
"},{"location":"deployment/container/#1-setup-closed-network","title":"1. Setup closed network","text":"First, we want to create the internal network:
docker network create -d bridge optimade\n
"},{"location":"deployment/container/#2-run-mongodb-service","title":"2. Run MongoDB service","text":"Then, we can create a volume for the MongoDB server to use and start it.
Note
This can be setup in other ways, binding to local paths or otherwise. For more information on the specifics of how to use the MongoDB container image, see the Docker Hub documentation. For more information from the MongoDB team concerning the Enterprise version, see the MongoDB documentation.
docker volume create mongodb-persist\n# `mongo` will be the host name in the docker network, using `--name`.\ndocker run \\\n --detach \\\n --name mongo \\\n --volume mongodb-persist:/data/db \\\n --network optimade \\\n docker.io/library/mongo:latest\n
At this point you should fill up the database with your data, or instead of doing the command above exactly, you could choose to bind to another \"local\" path that contains an existing MongoDB you have.
Most likely, you will have a database dump archive, which you will now be able to import. If you do so, please note the name of the database, the collection, and consider any data model mappings from your data to OPTIMADE data models for structures and/or references.
In the example, we assume the database has been filled with fully valid OPTIMADE structures and references in a database called optimade_prod, within the collections structures and references, respectively.
"},{"location":"deployment/container/#3-run-the-optimade-service","title":"3. Run the OPTIMADE service","text":"With this information, we can now start the OPTIMADE server:
docker run \\\n --rm \\\n --detach \\\n --publish 8081:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --network optimade \\\n --env OPTIMADE_CONFIG_FILE= \\\n --env optimade_insert_test_data=false \\\n --env optimade_database_backend=mongodb \\\n --env optimade_mongo_uri=mongodb://mongo:27017 \\\n --env optimade_mongo_database=optimade_prod \\\n --env optimade_references_collection=references \\\n --env optimade_structures_collection=structures \\\n --env optimade_page_limit=25 \\\n --env optimade_page_limit_max=100 \\\n --env optimade_base_url=http://localhost:8081 \\\n --env optimade_index_base_url=http://localhost:8080 \\\n --env optimade_provider=\"{\\\"prefix\\\":\\\"myorg\\\",\\\"name\\\":\\\"My Organization\\\",\\\"description\\\":\\\"Short description for My Organization\\\",\\\"homepage\\\":\\\"https://example.org\\\"}\" \\\n ghcr.io/materials-consortia/optimade:latest\n
Note, the optimade_base_url
and optimade_index_base_url
values should be different if the server is run through a reverse-proxy service like NGINX, Apache or other. For this example, we are only concerned with exposing the OPTIMADE APIs from the localhost
.
Furthermore, note that we \"unset\" the OPTIMADE_CONFIG_FILE
environment variable to ensure the system defaults are used instead of configuration values that matches the test data.
The next step is to start the OPTIMADE index meta-database. For this, we will first create a JSON file to reference and load as data for the /links
endpoint:
[\n {\n \"id\": \"my-optimade-db\",\n \"type\": \"links\",\n \"name\": \"My OPTIMADE API\",\n \"description\": \"An OPTIMADE API for my database of essential material structures.\",\n \"base_url\": \"http://localhost:8081\",\n \"homepage\": \"https://example.org\",\n \"link_type\": \"child\"\n }\n]\n
This file is stored in a newly created directory at /home/user/optimade/index_data/index_links.json
.
Now, we can start the index meta-database server:
# `optimade_insert_test_data` needs to be `true` to insert JSON file data\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main_index \\\n --name my-optimade-index \\\n --network optimade \\\n --env OPTIMADE_CONFIG_FILE= \\\n --env optimade_insert_test_data=true \\\n --env optimade_database_backend=mongodb \\\n --env optimade_mongo_uri=mongodb://mongo:27017 \\\n --env optimade_mongo_database=optimade_index_prod \\\n --env optimade_links_collection=links \\\n --env optimade_page_limit=25 \\\n --env optimade_page_limit_max=100 \\\n --env optimade_base_url=http://localhost:8080 \\\n --env optimade_index_base_url=http://localhost:8080 \\\n --env optimade_provider=\"{\\\"prefix\\\":\\\"myorg\\\",\\\"name\\\":\\\"My Organization\\\",\\\"description\\\":\\\"Short description for My Organization\\\",\\\"homepage\\\":\\\"https://example.org\\\"}\" \\\n --env optimade_default_db=my-optimade-db \\\n --env optimade_index_links_path=/external/index_data/index_links.json \\\n --volume /home/user/optimade/index_data:/external/index_data \\\n ghcr.io/materials-consortia/optimade:latest\n
"},{"location":"deployment/container/#5-test-the-services","title":"5. Test the services","text":"Finally, we can test it all works as intended.
Go to localhost:8080/links to check you see the list of OPTIMADE databases linked to by the index meta-database. This list should include the entry from the JSON file above with ID my-optimade-db
.
You could from there go to localhost:8081/info and see the introspective information about the OPTIMADE database. Furthermore, now you could go search through the material structures in your database, e.g., retrieving all structures that include carbon and oxygen in their list of chemical elements: localhost:8081/structures?filter=elements HAS ALL \"C\",\"O\".
It is also good to test the limits we specified are upheld, e.g., the maximum number of requested entries is not allowed to exceed 100 according to the optimade_page_limit_max
value we have specified. To test this, we could request a response with 101 entries, which should return an error: localhost:8081/structures?page_limit=101. And then a request for a response with 101 entries, which should not return an error: localhost:8081/structures?page_limit=100.
If you wish to inspect the logs of any service, you can use the docker logs
command, followed by any of the service container names, e.g., docker logs my-optimade
will display the logged stdout
and stderr
from the OPTIMADE database server.
Use the test data
If you do not have any data with which to fill up the MongoDB backend, you can run through the example using test data with some minor changes. However, it is crucial you first git clone
the repository locally, since the test data is included only in the repository - not the container image.
Run all docker
commands from the root of the cloned repository.
When running the OPTIMADE servers, leave out the line concerning \"unsetting\" the OPTIMADE_CONFIG_FILE
environment variable. When first running the OPTIMADE server (not the index meta-database) set the optimade_insert_test_data
value to true
. If you stop the server and want to restart it, you should then set the variable to false
, since the startup of the server will otherwise fail, due to the test data already existing in the MongoDB database collections and the subsequent re-insertion will throw an error.
The optimade
package can be used to create a standalone web application that serves the OPTIMADE API based on a pre-configured MongoDB backend. In this document, we are going to use optimade
differently and use it to add an OPTIMADE API implementation alongside an existing API that employs an Elasticsearch storage layer.
Let's assume we already have a FastAPI application that runs an unrelated web service, and that we use an Elasticsearch backend that contains all structure data, but not necessarily in a form that OPTIMADE expects.
"},{"location":"deployment/integrated/#providing-the-optimade-configuration","title":"Providing theoptimade
configuration","text":"optimade
can read its configuration from a JSON file. It uses the OPTIMADE_CONFIG_FILE
environment variable (or a default path) to find the config file. If you run optimade
code inside another application, you might want to provide this config file as part of the source code and not via environment variables. Let's say you have a file optimade_config.json
as part of the Python module that you use to create your OPTIMADE API.
Tip
You can find more detailed information about configuring the optimade
server in the Configuration section.
Before importing any optimade
modules, you can set the OPTIMADE_CONFIG_FILE
environment variable to refer to your config file:
import os\nfrom pathlib import Path\n\nos.environ['OPTIMADE_CONFIG_FILE'] = str(Path(__file__).parent / \"optimade_config.json\")\n
"},{"location":"deployment/integrated/#customize-the-entrycollection-implementation","title":"Customize the EntryCollection
implementation","text":"Let's assume that your Elasticsearch backend stores structure data in a different enough manner that you need to provide your own custom implementation. The following code customizes the EntryCollection
class for structures, whilst keeping the default MongoDB-based implementation (using MongoCollection
) for all other entry types.
from optimade.server.routers import structures\n\nstructures.structures_coll = MyElasticsearchStructureCollection()\n
You can imagine that MyElasticsearchStructureCollection
either sub-classes the default optimade
Elasticsearch implementation (ElasticsearchCollection
) or sub-classes EntryCollection
, depending on how deeply you need to customize the default optimade
behavior.
Let's assume you have an existing FastAPI app my_app
. It already implements a few routers under certain path prefixes, and now you want to add an OPTIMADE implementation under the path prefix /optimade
.
First, you have to set the root_path
in the optimade
configuration, so that the app expects all requests to be prefixed with /optimade
.
Second, you simply mount the optimade
app into your existing app my_app
:
from optimade.server.config import CONFIG\n\nCONFIG.root_path = \"/optimade\"\n\nfrom optimade.server import main as optimade\n\noptimade.add_major_version_base_url(optimade.app)\nmy_app.mount(\"/optimade\", optimade.app)\n
Tip
In the example above, we imported CONFIG
before main
so that our config was loaded before app creation. To avoid the need for this, the root_path
can be set in your JSON config file, passed as an environment variable, or declared in a custom Python module (see Configuration).
See also the FastAPI documentation on sub-applications.
Now, if you run my_app
, it will still serve all its routers as before and in addition it will also serve all OPTIMADE routes under /optimade/
and the versioned URLs /optimade/v1/
.
This package includes a Python client that can be used to query multiple OPTIMADE APIs simultaneously, whilst automatically paginating through the results of each query. The client can be used from the command-line (optimade-get
), or called in Python code.
The client does not currently validate the returned data it comes back from the databases, and as some OPTIMADE APIs do not implement all features, it is worth paying attention to any error messages emitted by each database.
"},{"location":"getting_started/client/#features","title":"Features","text":"This list outlines the current and planned features for the client:
The client requires some extra dependencies that can be installed with the PyPI package with
pip install optimade[http_client]\n
or from a local copy of the repository with pip install -e .[http_client]\n
"},{"location":"getting_started/client/#usage","title":"Usage","text":"By default, the client will query all OPTIMADE API URLs that it can find via the Providers list:
Command linePythonoptimade-get --filter 'elements HAS \"Ag\"'\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nresults = client.get('elements HAS \"Ag\"')\n
At the command line, it may be immediately useful to redirect or save these results to a file:
# Save the results to a JSON file directly\noptimade-get --filter 'elements HAS \"Ag\"' --output-file results.json\n# or redirect the results (in a POSIX shell)\noptimade-get --filter 'elements HAS \"Ag\"' > results.json\n
We can refine the search by manually specifying some URLs:
Command linePythonoptimade-get --output-file results.json https://optimade.fly.dev https://optimade.odbx.science\n
from optimade.client import OptimadeClient\nclient = OptimadeClient(\n base_urls=[\"https://optimade.fly.dev\", \"https://optimade.odbx.science\"]\n)\nclient.get()\n
or by including/excluding some providers by their registered IDs in the Providers list.
Query only a list of included providers (after a lookup of the providers list):
Command linePython# Only query databases served by the example providers\noptimade-get --include-providers exmpl,optimade\n
# Only query databases served by the example providers\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n include_providers={\"exmpl\", \"optimade\"},\n)\nclient.get()\n
Exclude certain providers:
Command linePython# Exclude example providers from global list\noptimade-get --exclude-providers exmpl,optimade\n
# Exclude example providers from global list\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n exclude_providers={\"exmpl\", \"optimade\"},\n)\nclient.get()\n
Exclude particular databases by URL:
Command linePython# Exclude specific example databases\noptimade-get --exclude-databases https://example.org/optimade,https://optimade.org/example\n
# Exclude specific example databases\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n exclude_databases={\"https://example.org/optimade\", \"https://optimade.org/example\"}\n)\nclient.get()\n
"},{"location":"getting_started/client/#filtering","title":"Filtering","text":"By default, an empty filter will be used (which will return all entries in a database). You can specify your desired filter as follows (note the quotation marks):
Command linePythonoptimade-get --filter 'elements HAS \"Ag\" AND nsites < 2' --output-file results.json\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nclient.get('elements HAS \"Ag\" AND nsites < 2')\n
The filter will be validated against the optimade-python-tools
reference grammar before it is sent to the underlying servers.
At the command-line, the results of the query will be printed to stdout
, ready to be redirected to a file or piped into another program. For example:
optimade-get --filter 'nsites = 1' --output-file results.json https://optimade.fly.dev\ncat results.json\n
has the following (truncated) output:
{\n // The endpoint that was queried\n \"structures\": {\n // The filter applied to that endpoint\n \"nsites = 1\": {\n // The base URL of the OPTIMADE API\n \"https://optimade.fly.dev\": {\n // The OPTIMADE API response as if called with an infinite `page_limit`\n \"data\": [\n {\n \"id\": \"mpf_1\",\n \"type\": \"structures\",\n \"attributes\": {\n ...\n }\n \"relationships\": {\n ...\n }\n },\n ...\n ],\n \"errors\": [],\n \"links\": {\n \"next\": null,\n \"prev\": null\n },\n \"included\": [\n ...\n ],\n \"meta\": {\n ...\n }\n }\n }\n }\n}\n
The response is broken down by queried endpoint, filter and then base URL so that the query URL can be easily reconstructed. This is the same format as the cached results of the Python client:
from optimade.client import OptimadeClient\nclient = OptimadeClient(base_urls=\"https://optimade.fly.dev\")\nclient.get('nsites = 1')\nclient.get('nsites = 2')\nprint(client.all_results)\n
will return a dictionary with top-level keys:
{\n \"structures\": {\n \"nsites = 1\": {\n \"https://optimade.fly.dev\": {...}\n },\n \"nsites = 2\": {\n \"https://optimade.fly.dev\": {...}\n }\n }\n}\n
For a given session, this cache can be written and reloaded into an OPTIMADE client object to avoid needing to repeat queries.
Info
In a future release, this cache will be automatically restored from disk and will obey defined cache lifetimes.
"},{"location":"getting_started/client/#querying-other-endpoints","title":"Querying other endpoints","text":"The client can also query other endpoints, rather than just the default /structures
endpoint. This includes any provider-specific extensions/<example>
endpoints that may be implemented at a given base URL, which can be found listed at the corresponding /info
endpoint for that database.
In the CLI, this is done with the --endpoint
flag. In the Python interface, the different endpoints can be queried as attributes of the client class or equivalently as a paramter to client.get()
or client.count()
(see below).
optimade-get --endpoint \"structures\"\noptimade-get --endpoint \"references\"\noptimade-get --endpoint \"info\"\noptimade-get --endpoint \"info/structures\"\noptimade-get --endpoint \"extensions/properties\"\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\n\nclient.references.count()\nclient.count(endpoint=\"references\")\n\nclient.info.get()\nclient.get(endpoint=\"info\")\n\nclient.info.structures.get()\nclient.get(endpoint=\"info/structures\")\n\nclient.extensions.properties.get()\nclient.get(endpoint=\"extensions/properties\")\n
"},{"location":"getting_started/client/#listing-the-properties-served-by-a-database","title":"Listing the properties served by a database","text":"You can also retrieve the list of properties served by a database using the --list-properties
flag:
optimade-get --list-properties structures --include-providers odbx\n
from optimade.client import OptimadeClient\nclient = OptimadeClient(include_providers={\"odbx\"})\nclient.list_properties(\"structures\")\n
and do simple string matching filtering of the response:
Command linePythonoptimade-get --list-properties structures --search-property gap\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nclient.list_properties(\"structures\")\n
"},{"location":"getting_started/client/#limiting-the-number-of-responses","title":"Limiting the number of responses","text":"Querying all OPTIMADE APIs without limiting the number of entries can result in a lot of data. The client will limit the number of results returned per database to the value of max_results_per_provider
(defaults: 1000 for Python, 10 for CLI). This limit will be enforced up to a difference of the default page limit for the underlying OPTIMADE API (which is used everywhere). This parameter can be controlled via the --max-results-per-provider 10
at the CLI, or as an argument to OptimadeClient(max_results_per_provider=10)
.
Setting this to a value of -1
or 0
(or additionally None
, if using the Python interface) will remove the limit on the number of results per provider. In the CLI, this setting should be used alongside --output-file
or redirection to avoid overflowing your terminal!
Downloading all the results for a given query can require hundreds or thousands of requests, depending on the number of results and the database's page limit. It is possible to just count the number of results before downloading the entries themselves, which only requires 1 request per database. This is achieved via the --count
flag in the CLI, or the .count()
method in the Python interface. We can use this to repeat the queries from the OPTIMADE paper:
optimade-get \\\n --count \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\"' \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\" AND nelements=2' \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\" AND NOT elements HAS \"Pb\" AND elements LENGTH 3'\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nfilters = [\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\"',\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\" AND nelements=2'\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\" AND NOT elements HAS \"Pb\" AND elements LENGTH 3'\n]\nfor f in filters:\n client.count(f)\n
which, at the timing of writing, yields the results:
{\n \"structures\": {\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\", \\\"Pb\\\"\": {\n \"http://aflow.org/API/optimade/\": null,\n \"https://www.crystallography.net/cod/optimade\": 436062,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 487,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 1427,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 3719,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 9592,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 1093,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 4395,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 348,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 503,\n \"https://optimade.materialsproject.org\": 30351,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 4451056,\n \"https://optimade.odbx.science\": 55,\n \"http://optimade.openmaterialsdb.se\": 58718,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 2632,\n \"http://optimade.2dmatpedia.org\": 1172\n },\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\", \\\"Pb\\\" AND nelements=2\": {\n \"http://aflow.org/API/optimade/\": 63011,\n \"https://www.crystallography.net/cod/optimade\": 3968,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 2,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 779,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 334,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 1566,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 276,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 24,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 0,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 0,\n \"https://optimade.materialsproject.org\": 3728,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 587923,\n \"https://optimade.odbx.science\": 54,\n \"http://optimade.openmaterialsdb.se\": 690,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 296,\n \"http://optimade.2dmatpedia.org\": 739\n },\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\" AND NOT elements HAS \\\"Pb\\\" AND elements LENGTH 3\": {\n \"http://aflow.org/API/optimade/\": null,\n \"https://www.crystallography.net/cod/optimade\": 33776,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 0,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 378,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 144,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 4398,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 74,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 1447,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 0,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 0,\n \"https://optimade.materialsproject.org\": 11559,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 2092989,\n \"https://optimade.odbx.science\": 0,\n \"http://optimade.openmaterialsdb.se\": 7428,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 661,\n \"http://optimade.2dmatpedia.org\": 255\n }\n }\n}\n
"},{"location":"getting_started/client/#callbacks","title":"Callbacks","text":"In Python, the client can also be initialized with a list of callbacks. These will be executed after every request and will have access to the JSON response and the request URL.
For example, callbacks be used, to save to a file or write to a database, without having to pull all the results into memory. Care should be taken if combining an asynchronous client with callbacks, as the callbacks may depend on the execution order of various asynchronous requests and the callbacks themselves may be blocking.
Python# Write a callback that saves into a MongoDB (fake or otherwise)\nimport mongomock as pymongo\nfrom optimade.client import OptimadeClient\n\ndb = pymongo.MongoClient().database.structures\n\ndef write_to_db(url, results):\n for entry in results[\"data\"]:\n entry.update(entry.pop(\"attributes\"))\n entry[\"immutable_id\"] = url\n db.insert_one(entry)\n\nclient = OptimadeClient(callbacks=[write_to_db], silent=True)\n\nclient.get()\n\nprint(db.find_one())\n
Callbacks can also optionally return a dictionary of data that can be used by the client.
Currently the supported keys are:
next
(string): To override the next URL returned by the API. This can be used to dynamically change queries based on the results, or to skip pages that have already been queried previously.advance_results
(integer): To skip the progress bar forward by the set amount.When multiple callbacks are provided, only the final callback will have its result captured.
"},{"location":"getting_started/setting_up_an_api/","title":"Setting up an OPTIMADE API","text":"These notes describe how to set up and customize an OPTIMADE API based on the reference server in this package for some existing crystal structure data.
To follow this guide, you will need to have a working development installation, as described in the installation instructions. Complete examples of APIs that use this package are described in the Use Cases section.
"},{"location":"getting_started/setting_up_an_api/#setting-up-the-database","title":"Setting up the database","text":"The optimade
reference server requires a data source per OPTIMADE entry type (structures
, references
, links
). In the simplest case, these can be configured as named MongoDB collections with a defined MongoDB URI and database name (see below), but they can also be set up as custom subclasses of EntryCollection
that could simply read from a static file. In the reference server, these data sources, or collections, are created in the submodule for the corresponding routers/endpoints.
Here, we shall use the built-in MongoDB collections for each entry type, by simply specifying the appropriate options in the configuration, namely \"database_backend\": \"mongodb\"
, \"mongo_uri\": \"mongodb://localhost:27017\"
, \"mongo_database\": \"optimade\"
and the collection names for each entry type (\"structures_collection\": \"structures\"
etc.). These notes will now assume that you have a MongoDB instance running and you have created a database that matches your \"mongo_database\"
config option.
If you disable inserting test data (with the \"insert_test_data\": false
configuration option), you can test your API/database connection by running the web server with uvicorn optimade.server.main:app --port 5000
and visiting the (hopefully empty) structures endpoint at localhost:5000/v1/structures
(or your chosen base URL).
Note
As of version v0.16, the other supported database backend is Elasticsearch. If you are interested in using another backend, or would like it to be supported in the optimade
package, please raise an issue on GitHub and visit the notes on implementing new filter transformers.
There are two ways to work with data that does not exactly match the OPTIMADE specification, both of which require configuring a subclass of BaseResourceMapper
that converts your stored data format into an OPTIMADE-compliant entry. The two options are:
The main consideration when choosing these options is not necessarily how closely your data matches the OPTIMADE format, but instead how readily the OPTIMADE filtering of that document can be mapped into the corresponding database query. This could require writing or extending the BaseFilterTransformer
class, which takes an OPTIMADE filter string and converts it into a backend-specific query.
For example, if your database stores chemical formulae with extraneous \"1\"'s, e.g., SiO2 is represented as \"Si1O2\"
, then the incoming OPTIMADE filter (which asserts that elements must be alphabetical, and \"1\"'s must be omitted) for chemical_formula_reduced=\"O2Si\"
will also need to be transformed so that the corresponding database query matches the stored string, which in this case can be done easily. Instead, if you are storing chemical formulae as an unreduced count per simulation cell, e.g., \"Si4O8\"
, then it is impossible to remap the filter chemical_formula_reduced=\"O2Si\"
such that it matches all structures with the correct formula unit (e.g., \"SiO2\"
, \"Si2O4\"
, ...). This would then instead require option 2 above, namely either the addition of auxiliary fields that store the correct (or mappable) OPTIMADE format in the database, or the creation of a secondary database that returns the pre-converted structures.
In the simplest case, the mapper classes can be used to define aliases between fields in the database and the OPTIMADE field name; these can be configured via the aliases
option as a dictionary mapping stored in a dictionary under the appropriate endpoint name, e.g. \"aliases\": {\"structures\": {\"chemical_formula_reduced\": \"my_chem_form\"}}
, or defined as part of a custom mapper class.
In either option, you should now be able to insert your data into the corresponding MongoDB (or otherwise) collection.
"},{"location":"getting_started/setting_up_an_api/#serving-custom-fieldsproperties","title":"Serving custom fields/properties","text":"According to the OPTIMADE specification, any field not standardized in the specification must be prefixed with an appropriate \"provider prefix\" (e.g., \"_aflow
\" for AFLOW and \"_cod
\" for COD). This prefix is intended to be unique across all OPTIMADE providers to enable filters to work across different implementations. The prefix can be set in the configuration as part of the provider
option.
Once the prefix has been set, custom fields can be listed by endpoint in the provider_fields
configuration option. Filters that use the prefixed form of these fields will then be passed through to the underlying database without the prefix, and then the prefix will be reinstated in the response.
Example
Example JSON config file fragment for adding two fields to each of the structures
and references
endpoints, that will be served as, e.g., _exmpl_cell_volume
if the provider.prefix
is set to 'exmpl'.
\"provider_fields\": {\n \"structures\": [\"cell_volume\", \"total_energy\"],\n \"references\": [\"orcid\", \"num_citations\"],\n}\n
It is recommended that you provide a description, type and unit for each custom field that can be returned at the corresponding /info/<entry_type>
endpoint. This can be achieved by providing a dictionary per field at provider_fields
, rather than a simple list.
Example
Example JSON config file fragment for adding a description, type and unit for the custom _exmpl_cell_volume
field, which will cause it to be added to the /info/structures
endpoint.
\"provider_fields\": {\n \"structures\": [\n {\n \"name\": \"cell_volume\",\n \"description\": \"The volume of the cell per formula unit.\",\n \"unit\": \"Ao3\",\n \"type\": \"float\"\n },\n \"total_energy\"\n ],\n \"references\": [\"orcid\", \"num_citations\"],\n}\n
"},{"location":"getting_started/setting_up_an_api/#extending-the-pydantic-models","title":"Extending the pydantic models","text":"The pydantic models can also be extended with your custom fields. This can be useful for validation, and for generating custom OpenAPI schemas for your implementation. To do this, the underlying EntryResourceAttributes
model will need to be sub-classed, the pydantic fields added to that class, and the server adjusted to make use of those models in responses. In this case, it may be easier to write a custom endpoint for your entry type, that copies the existing reference endpoint.
Your custom model will need to be registered in three places:
ENTRY_INFO_SCHEMAS
dictionary.Finally, the model must be instructed to use the prefixed (aliased) fields when generating its schemas.
Pulling all of this together:
from optimade.server.schemas import ENTRY_INFO_SCHEMAS\nfrom optimade.models import (\n StructureResource, StructureResourceAttributes, OptimadeField\n)\n\n\nclass MyStructureResourceAttributes(StructureResourceAttributes):\n my_custom_field: str = OptimadeField(\n \"default value\",\n description=\"This is a custom field\",\n )\n\n class Config:\n \"\"\"Add a pydantic `Config` that defines the alias generator,\n based on our configured `provider_fields`.\n\n \"\"\"\n @classmethod\n def alias_generator(cls, name: str) -> str:\n if name in CONFIG.provider_fields.get(\"structures\", []):\n return f\"_{CONFIG.provider.prefix}_{name}\"\n return name\n\n\nclass MyStructureResource(StructureResource):\n attributes: MyStructureResourceAttributes\n\n\nENTRY_INFO_SCHEMAS[\"structures\"] = MyStructureResource.schema\n
Currently, the reference server is not flexible enough to use custom response classes via configuration only (there is an open issue tracking this #929), so instead the code will need to be forked and modified for your implementation.
Note
A similar procedure can be followed for the URL query parameter classes EntryListingQueryParams
and SingleEntryQueryParams
so that custom query parameters can be defined for your API. The individual API routes then need to be adjusted to use the custom query parameter classes. By default, the reference server will validate the incoming query parameters against these classes. If you want to use custom query parameters without redefining the classes mentioned above, you can disable this behaviour by setting the configuration option validate_query_parameters
to false, after which all query parameters will be passed on to the corresponding router method (e.g., database queries).
With the database collections, mappers, aliases and provider configured, you can try running the web server (with e.g., uvicorn optimade.server.main:app
, if your app is in the same file as the reference server) and validating it as an OPTIMADE API, following the validation guide.
If you host your API at a persistent URL, you should consider registering as an OPTIMADE provider, which will add you to the federated list used by users and clients to discover data. Instructions for how to do this can be found at in the Materials-Consortia/providers repository.
"},{"location":"getting_started/use_cases/","title":"Example use cases","text":""},{"location":"getting_started/use_cases/#serving-a-single-database","title":"Serving a single database","text":"The Materials Project uses optimade-python-tools
alongside their existing API and MongoDB database, providing OPTIMADE-compliant access to highly-curated density-functional theory calculations across all known inorganic materials.
optimade-python-tools
handles filter parsing, database query generation and response validation by running the reference server implementation with minimal configuration.
odbx, a small database of results from crystal structure prediction calculations, follows a similar approach. This implementation is open source, available on GitHub at ml-evs/odbx.science.
"},{"location":"getting_started/use_cases/#serving-multiple-databases","title":"Serving multiple databases","text":"Materials Cloud uses optimade-python-tools
as a library to provide an OPTIMADE API entry to archived computational materials studies, created with the AiiDA Python framework and published through their archive. In this case, each individual study and archive entry has its own database and separate API entry. The Python classes within the optimade
package have been extended to make use of AiiDA and its underlying PostgreSQL storage engine.
Details of this implementation can be found on GitHub at aiidateam/aiida-optimade.
"},{"location":"getting_started/use_cases/#extending-an-existing-api","title":"Extending an existing API","text":"NOMAD uses optimade-python-tools
as a library to add OPTIMADE API endpoints to an existing web app. Their implementation uses the Elasticsearch database backend to filter on millions of structures from aggregated first-principles calculations provided by their users and partners. NOMAD also uses the package to implement a GUI search bar that accepts the OPTIMADE filter language. NOMAD uses the release versions of the optimade-python-tools
package, performing all customisation via configuration and sub-classing. The NOMAD OPTIMADE API implementation is available in the NOMAD FAIR GitLab repository.
This use case is demonstrated in the example Integrate OPTIMADE with an existing web application.
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#optimade-python-tools","title":"OPTIMADE Python tools","text":"Latest releaseBuild statusActivityThe aim of OPTIMADE is to develop a common API, compliant with the JSON:API 1.0 specification. This is to enable interoperability among databases that serve crystal structures and calculated properties of existing and hypothetical materials.
This repository contains a library of tools for implementing and consuming OPTIMADE APIs using Python:
optimade.adapters
module.optimade-get
) that can query multiple OPTIMADE providers concurrently with a given filter, at the command-line or from Python code.optimade-validator
) or used as a GitHub Action from optimade-validator-action; this validator is used to construct the providers dashboard.This document, guides, and the full module API documentation can be found online at https://optimade.org/optimade-python-tools. In particular, documentation of the OPTIMADE API response data models (implemented here with pydantic) can be found online under OPTIMADE Data Models.
The release history and changelog can be found in the changelog.
"},{"location":"#installation","title":"Installation","text":"Detailed installation instructions for different use cases (e.g., using the library or running a server) can be found in the installation documentation.
The latest stable version of this package can be obtained from PyPI:
pip install optimade\n
The latest development version of this package can be obtained from the main branch of this repository:
git clone https://github.com/Materials-Consortia/optimade-python-tools\n
"},{"location":"#supported-optimade-versions","title":"Supported OPTIMADE versions","text":"Each release of the optimade
package from this repository only targets one version of the OPTIMADE specification, summarised in the table below.
optimade
requirements v1.0.0 optimade<=0.12.9
v1.1.0 optimade~=0.16
"},{"location":"#contributing-and-getting-help","title":"Contributing and Getting Help","text":"All development of this package (bug reports, suggestions, feedback and pull requests) occurs in the optimade-python-tools GitHub repository. Contribution guidelines and tips for getting help can be found in the contributing notes.
"},{"location":"#how-to-cite","title":"How to cite","text":"If you use this package to access or serve OPTIMADE data, we kindly request that you consider citing the following:
openapi.json
and index_openapi.json
.openapi.json
(see also interactive JSON editor).openapi.json
specification.Full Changelog
"},{"location":"CHANGELOG/#v113-2024-09-16","title":"v1.1.3 (2024-09-16)","text":"Full Changelog
"},{"location":"CHANGELOG/#v112-2024-09-03","title":"v1.1.2 (2024-09-03)","text":"Full Changelog
Fixed bugs:
Merged pull requests:
main
#2136 (ml-evs)ad0e214
to e90caba
#2131 (dependabot[bot]).from_url
method to base adapter for easily loading a single entry #2122 (ml-evs)Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
--random-seed
for deterministic validation #2096 (merkys)Full Changelog
Closed issues:
Merged pull requests:
skip_ssl
flag/option to client #2086 (ml-evs)Full Changelog
Closed issues:
Merged pull requests:
Structure
converter #2021 (ml-evs)Full Changelog
Fixed bugs:
requests
timeout when getting providers list #1955 (ml-evs)Merged pull requests:
46c8d85
to ad0e214
#1948 (dependabot[bot])97c69be
to 46c8d85
#1946 (dependabot[bot])93d2b52
to 97c69be
#1941 (dependabot[bot])members: true
#1936 (ml-evs)Full Changelog
Fixed bugs:
requests.get
in get_providers
#1954LinksResource
#939Closed issues:
Full Changelog
"},{"location":"CHANGELOG/#v102-2024-01-11","title":"v1.0.2 (2024-01-11)","text":"Full Changelog
Implemented enhancements:
data_returned
is not available #1924Merged pull requests:
d8322a4
to 93d2b52
#1926 (dependabot[bot])564a499
to d8322a4
#1909 (dependabot[bot])Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
optimade-python-tools has reached v1.0.0!
We have decided to make this first major release at this point due to the arduous migration to pydantic v2 between v1.0.0 and v0.25.3. This will allow for improved compatibility with the rest of the ecosystem, plus all of the performance and ergonomics benefits of the new pydantic.
If you are using optimade-python-tools primarily as a library, then you may need to make some code changes to support the new version. For example, the underlying API for dealing with the pydantic models has changed (e.g., model.dict()
is now model.model_dump()
) -- a full migration guide can be found in the pydantic docs. Additionally, any cases where the underlying JSON schemas were being modified may need to be updated; internally we are pretty much exclusively operating on the pydantic model objects without needing to modify the raw schemas anymore.
Going forward, v1.0.x will be the last series to support v1.1 of the OPTIMADE specification, with future versions of the package v1.x.y adding features from the pre-release of OPTIMADE v1.2.
A branch will be maintained for the v0.25.x series will be maintained for those who are stuck on pydantic v1 and run into bugs. Please make it clear in any bug reports if you are using this version.
Fixed bugs:
Closed issues:
nullable
from schemas (for OpenAPI 3.1) #1814Merged pull requests:
--version
#1848 (ml-evs)Full Changelog
Merged pull requests:
links->next
issues #1794 (ml-evs)mongo_count_timeout
to skip the global count per request #1757 (ml-evs)Full Changelog
Merged pull requests:
Full Changelog
This release simply tests the migration to pyproject.toml
, as well as updating a few dependencies along the way.
Merged pull requests:
Full Changelog
This release primarily contains changes to the server code including significant optimizations, the ability to fully disable validation of outgoing responses and the ability to configure provider fields that already exist in the database with a prefix, as well as various bug fixes and dependency compatibility updates. Other important changes include the dropping of Python 3.8 support. This version still uses Pydantic v1 and a future version will migrate fully to Pydantic v2.
Fixed bugs:
elements_ratios
length alias bug #1713 (JPBergsma)Closed issues:
Merged pull requests:
[all_strict]
deps set that reads from requirements files #1719 (ml-evs)1e5d59d
to 564a499
#1696 (dependabot[bot])1e5d59d
to 564a499
#1692 (dependabot[bot])1e5d59d
to 564a499
#1674 (dependabot[bot])Full Changelog
Implemented enhancements:
--list-properties
and --search-property
functionality to client #1663 (ml-evs)Merged pull requests:
54c4f05
to 1e5d59d
#1654 (dependabot[bot])1c3f0f1
to 1e5d59d
#1639 (dependabot[bot])Full Changelog
Implemented enhancements:
LENGTH
queries with validator #1637 (ml-evs)Closed issues:
Merged pull requests:
ae359c2
to 1c3f0f1
#1638 (dependabot[bot])54c4f05
to ae359c2
#1622 (dependabot[bot])Full Changelog
Merged pull requests:
Full Changelog
Fixed bugs:
Merged pull requests:
page_above
in base entry collections #1560 (ml-evs)Full Changelog
Closed issues:
--skip-optional
#1558Merged pull requests:
--skip-optional
#1559 (ml-evs)a92e5bc
to 3be73e9
#1538 (dependabot[bot])Full Changelog
This patch release changes the synchronous functionality of OptimadeClient
to use \"vanilla\" requests rather than httpx. This enables easier use inside of Jupyter notebooks.
Closed issues:
OptimadeClient
in async mode with an existing event loop #1195Merged pull requests:
OptimadeClient
with requests #1536 (ml-evs)Full Changelog
This release adds a new feature to the reference server for avoiding validating data on the way out of the API (useful in cases where a small amount of leniency is desirable, and will have a minor performance bump). It also fixes a bug in the schema where page_below
and page_above
were codified as integers rather than strings.
Implemented enhancements:
Fixed bugs:
page_above/below
strings rather than ints #1529 (ml-evs)Full Changelog
This minor release contains new client functionality and improved support for ASE.
"},{"location":"CHANGELOG/#new-features","title":"New features:","text":"from optimade.client import OptimadeClient\n\nDATABASE = pymongo.MongoClient().database.collection\n\ndef save_callback(url, results) -> None:\n for structure in results[\"data\"]:\n DATABASE.insert_one(structure)\n\nclient = OptimadeClient(callbacks=[save_callback])\nclient.get()\n
Ability to create OPTIMADE structure objects from ASE atoms:
from optimade.adapters import Structure\nfrom ase import Atoms\n\nco = Atoms('CO', positions=[(0, 0, 0), (0, 0, 1.1)])\n\nstructure = Structure.from_ase_atoms(co)\n
Added ability to mute the client progress bars with --silent
/silent=True
and increased default response timeouts to better reflect those required for practical queries.
Implemented enhancements:
response_fields
behaviour #1514 (ml-evs)Closed issues:
Merged pull requests:
--silent
option to suppress client output until results #1518 (ml-evs)Full Changelog
This release primarily adds compatibility for the newest FastAPI releases (>=0.87
) by updating our test client to work with both httpx
and requests
.
Merged pull requests:
httpx
-based TestClient
for latest FastAPI version #1460 (ml-evs)Full Changelog
This release adds the ability to include or exclude particular providers from queries with the OptimadeClient
class and optimade-get
CLI, via the provider's registered prefix (#1412)
For example:
# Only query databases served by the example providers\noptimade-get --include-providers exmpl,optimade\n# Exclude example providers from global list\noptimade-get --exclude-providers exmpl,optimade\n
You can also now exclude particular databases by their URLs:
# Exclude specific example databases\noptimade-get --exclude-databases https://example.org/optimade,https://optimade.org/example\n
The release also includes some server enhancements and fixes: - Caching of /info/
and /info/<entry>
endpoint responses (#1433) - A bugfix for the entry mapper classes, which were sharing cache resources globally leading to poor utilization (#1435)
Implemented enhancements:
/info
and /info/<entry>
responses #1433 (ml-evs)Fixed bugs:
lru_cache
s on the mapper classes are subtly wrong #1434Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
get_child_databases
retriever #1411 (ml-evs)Full Changelog
This release continues the modularisation of the package by moving the server exceptions and warnings out into top-level modules, and removing the core dependency on FastAPI (now a server dependency only). This should allow for easier use of the optimade.models
and optimade.client
modules within other packages.
Aside from that, the package now supports Python 3.11, and our example server is now deployed at Fly.io rather than Heroku.
Implemented enhancements:
Fixed bugs:
Closed issues:
page_number
#1372Merged pull requests:
page_number
at model level #1369 (ml-evs)mypy
and isort
in pre-commit & CI #1346 (ml-evs)Full Changelog
This is a hotfix release for #1335, a bug regarding chunked responses triggered when using the latest FastAPI version.
Fixed bugs:
chunk_size
is not always set in middleware method #1335chunk_size
is properly set when chunking responses #1336 (ml-evs)Full Changelog
Implemented enhancements:
meta->schema
value automatically #1323 (ml-evs)Merged pull requests:
Full Changelog
Fixed bugs:
page_number
query parameter #1303 (ml-evs)Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
from_pymatgen
structure adapter method and concept of ingesters #1296 (ml-evs)lru_cache
to many mapper properties #1245 (ml-evs)Merged pull requests:
Full Changelog
This minor release includes several usability improvements for the server and client arising from the OPTIMADE workshop. This release also drops support for Python 3.7, which should allow us to streamline our dependencies going forward.
Implemented enhancements:
Fixed bugs:
@classproperty
#1219Closed issues:
meta->schema
#1209meta->schemas
field to reference server #1208Merged pull requests:
fb05359
to a92e5bc
#1267 (dependabot[bot])lark
dependency to new name #1231 (ml-evs)schema_url
and index_schema_url
options #1210 (ml-evs)Full Changelog
This is a feature release that includes the new optimade.client.OptimadeClient
class, a client capable asynchronously querying multiple OPTIMADE APIs simultaneously. It also contains a patch for the OPTIMADE models that allows them to be used with more recent FastAPI versions without breaking OpenAPI 3.0 compatibility. Other changes can be found below. This release includes improvements to the validator to catch more cases where OPTIMADE APIs are only partially implemented. Previously, APIs that did not support filtering, pagination or limiting response fields at all (i.e., the query parameter is simply ignored) would pass most validation tests erroneously in some unlucky situations (#1180).
Implemented enhancements:
User-Agent
header #1187Fixed bugs:
OptimadeClient
crashes if an index meta-database is down #1196Merged pull requests:
User-Agent
with validator #1189 (ml-evs)fastapi>0.66
#1131 (ml-evs)Full Changelog
This release includes improvements to the validator to catch more cases where OPTIMADE APIs are only partially implemented. Previously, APIs that did not support filtering, pagination or limiting response fields at all (i.e., the query parameter is simply ignored) would pass most validation tests erroneously in some unlucky situations (#1180).
Fixed bugs:
Merged pull requests:
Full Changelog
This patch release adds a pre-built Docker container for the reference server to the GitHub Container Registry (GHCR) and a series of Deployment instructions in the online documentation.
The image can be easily pulled from GHCR with:
docker pull ghcr.io/materials-consortia/optimade
Implemented enhancements:
Closed issues:
Merged pull requests:
env
context for step #1178 (CasperWA)optimade
on ghcr.io #1171 (CasperWA)Full Changelog
This minor release contains fixes recommended for those deploying the optimade-python-tools reference server:
meta->data_returned
field was previously incorrect when using the MongoDB backend.validate_query_parameters
config option.nelements != 2
vs 2 != nelements
); this has now been fixed.Implemented enhancements:
immutable_id
#1142 (ml-evs)Fixed bugs:
meta->data_returned
is incorrect for paginated results with MongoDB #1140meta->data_returned
for paginated results with MongoDB #1141 (ml-evs)Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
species
from species_at_sites
when missing #1103 (ml-evs)Closed issues:
Full Changelog
Merged pull requests:
Full Changelog
Fixed bugs:
Security fixes:
Merged pull requests:
build
package to build distributions #1062 (CasperWA)Full Changelog
Implemented enhancements:
Security fixes:
Closed issues:
LarkParser
failing. #1037Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
chemical_formula_anonymous
validator accepts incorrect proportion order if started with 1 #1002Closed issues:
Merged pull requests:
chemical_formula_anonymous = AB2
to pass validation. #1001 (JPBergsma)diff
for checking PR body #1000 (CasperWA)Full Changelog
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
master
#971 (CasperWA)Full Changelog
Closed issues:
Merged pull requests:
357c27b
to fb05359
#945 (dependabot[bot])368f9f6
to 357c27b
#944 (dependabot[bot])91b51bd
to 368f9f6
#942 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
run.sh
does not appear to be available from the pip installation #904Merged pull requests:
Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
handle_response_fields()
#876 (CasperWA)Fixed bugs:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
fa25ed3
to 91b51bd
#858 (dependabot[bot])Full Changelog
Implemented enhancements:
Closed issues:
Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Implemented enhancements:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
7a54843
to fa25ed3
#791 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
alias_of
extracts alias wrongly #667Closed issues:
Merged pull requests:
e2074e8
to 7a54843
#741 (dependabot[bot])Full Changelog
Fixed bugs:
Merged pull requests:
da74513
to e2074e8
#727 (dependabot[bot])Full Changelog
Implemented enhancements:
Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
--as-type
in validator, one does not get a summary (--json
doesn't work) #699Merged pull requests:
Full Changelog
Implemented enhancements:
structure_features
#678Fixed bugs:
meta->data_available
#677Merged pull requests:
542ac0a
to da74513
#679 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Content-Type
value should be granularized according to RFC requirements in validator #669Merged pull requests:
fe5048b
to 542ac0a
#671 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
2673be6
to fe5048b
#633 (dependabot[bot])Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Closed issues:
Merged pull requests:
validatated
-> validated
#506 (merkys)Full Changelog
Fixed bugs:
Closed issues:
/links
endpoint for index meta-database #454data_returned
#402Merged pull requests:
/links
-endpoints #455 (CasperWA)Full Changelog
Implemented enhancements:
warnings
#105Fixed bugs:
/versions
endpoint content-type parameter \"header=present\" is provided in the wrong place #418Closed issues:
api_hint
query parameter #392Merged pull requests:
OptimadeWarning
s #431 (CasperWA)52027b1
to 9712dd8
#428 (dependabot[bot])api_hint
query parameter #421 (CasperWA)a96d424
to 52027b1
#389 (dependabot[bot])Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Merged pull requests:
Full Changelog
"},{"location":"CHANGELOG/#v096-2020-06-28","title":"v0.9.6 (2020-06-28)","text":"Full Changelog
Fixed bugs:
Full Changelog
"},{"location":"CHANGELOG/#v094-2020-06-26","title":"v0.9.4 (2020-06-26)","text":"Full Changelog
"},{"location":"CHANGELOG/#v093-2020-06-26","title":"v0.9.3 (2020-06-26)","text":"Full Changelog
Merged pull requests:
732593a
to a96d424
#368 (dependabot[bot])Full Changelog
Fixed bugs:
Closed issues:
type
field under properties
) #345Merged pull requests:
Full Changelog
"},{"location":"CHANGELOG/#v090-2020-06-17","title":"v0.9.0 (2020-06-17)","text":"Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
sortable
key under each property #273source_url
#260None
values in lattice_vectors
#170Merged pull requests:
sortable=True
to all properties #274 (CasperWA)Full Changelog
Fixed bugs:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
deps_clean-install
#247server.cfg
? #134Merged pull requests:
~
#246 (CasperWA)Full Changelog
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Implemented enhancements:
Fixed bugs:
Closed issues:
include
standard JSON API query parameter #94Merged pull requests:
include
query parameter #163 (CasperWA)Full Changelog
Implemented enhancements:
Fixed bugs:
response_fields
not working #154Closed issues:
page_page
to page_number
#165Merged pull requests:
Full Changelog
Implemented enhancements:
develop
or not? Default branch? - Create INSTALL.md #136Fixed bugs:
Closed issues:
Merged pull requests:
Full Changelog
Fixed bugs:
Merged pull requests:
JSONDecodeError
s in validator #144 (ml-evs)Full Changelog
Implemented enhancements:
Fixed bugs:
load_from_json
#137 (CasperWA)Merged pull requests:
Full Changelog
Merged pull requests:
Full Changelog
Implemented enhancements:
implementation
in top-level meta response #117Fixed bugs:
update-openapijson
is incomplete #123Closed issues:
sortable
field must be added to models #84Merged pull requests:
\"all\"
endpoint and rename collections submodule #73 (ml-evs)pre-commit
tool #53 (dwinston)Full Changelog
"},{"location":"CHANGELOG/#v011-2018-06-13","title":"v0.1.1 (2018-06-13)","text":"Full Changelog
"},{"location":"CHANGELOG/#v010-2018-06-05","title":"v0.1.0 (2018-06-05)","text":"Full Changelog
* This Changelog was automatically generated by github_changelog_generator
"},{"location":"CONTRIBUTING/","title":"Contributing and getting help","text":"If you run into any problems using this package, or if you have a question, suggestion or feedback, then please raise an issue on GitHub.
The Materials Consortia is very open to contributions across all of its packages. This may be anything from simple feedback and raising new issues to creating new PRs.
If you are interested in contributing but don't know where to begin, some issues have been marked with the good first issue label, typically where an isolated enhancement has a concrete suggestion. Simply add a comment under an issue if you are interested in tackling it!
Recommendations for setting up a development environment for this package can be found in the Installation instructions.
More broadly, if you would like to ask questions or contact the consortium about creating an OPTIMADE implementation for a new database, then please read the relevant \"get involved\" section on the OPTIMADE website.
"},{"location":"INSTALL/","title":"Installation","text":"This package can be installed from PyPI, or by cloning the repository, depending on your use-case.
optimade
Python package as a library, (e.g., using the models for validation, parsing filters with the grammar, or using the command-line tool optimade-validator
tool), it is recommended that you install the latest release of the package from PyPI with pip install optimade
. If you also want to use the OPTIMADE client to query OPTIMADE APIs, you should install with the additional dependencies: pip install optimade[http_client]
.pip install .
, or pip install -e .
for an editable installation). As an alternative, you can run the optimade
container image (see the Container image section below).This package may be used to setup and run an OPTIMADE index meta-database. Clone this repository and install the package locally with pip install -e .[server]
.
Info
To avoid installing anything locally and instead use the docker image, please see the section Container image below.
There is a built-in index meta-database set up to populate a mongomock
in-memory database with resources from a static json
file containing the child
resources you, as a database provider, want to serve under this index meta-database. The location of that json
file is controllable using the index_links_path
property of the configuration or setting via the environment variable optimade_index_links_path
.
Running the index meta-database is then as simple as writing ./run.sh index
in a terminal from the root of this package. You can find it at the base URL: http://localhost:5001/v1.
Here is an example of how it may look to start your server:
export OPTIMADE_CONFIG_FILE=/home/optimade_server/config.json\n./path/to/optimade/run.sh index\n
"},{"location":"INSTALL/#full-development-installation","title":"Full development installation","text":"The dependencies of this package can be found in setup.py
with their latest supported versions. By default, a minimal set of requirements are installed to work with the filter language and the pydantic
models. After cloning the repository, the install mode server
(i.e. pip install .[server]
) is sufficient to run a uvicorn
server using the mongomock
backend (or MongoDB with pymongo
, if present). The suite of development and testing tools are installed with via the install modes dev
and testing
. There are additionally two backend-specific install modes, elastic
and mongo
, as well as the all
mode, which installs all dependencies. All contributed Python code, must use the black code formatter, and must pass the flake8 linter that is run automatically on all PRs.
# Clone this repository to your computer\ngit clone --recursive git@github.com:Materials-Consortia/optimade-python-tools.git\ncd optimade-python-tools\n\n# Ensure a Python>=3.8 (virtual) environment (example below using Anaconda/Miniconda)\nconda create -n optimade python=3.10\nconda activate optimade\n\n# Install package and dependencies in editable mode (including \"dev\" requirements).\npip install -e \".[dev]\"\n\n# Optional: Retrieve the list of OPTIMADE providers. (Without this submodule, some of the tests will fail because \"providers.json\" cannot be found.)\ngit submodule update --init\n\n# Run the tests with pytest\npy.test\n\n# Install pre-commit environment (e.g., auto-formats code on `git commit`)\npre-commit install\n\n# Optional: Install MongoDB (and set `database_backend = mongodb`)\n# Below method installs in conda environment and\n# - starts server in background\n# - ensures and uses ~/dbdata directory to store data\nconda install -c anaconda mongodb\nmkdir -p ~/dbdata && mongod --dbpath ~/dbdata --syslog --fork\n\n# Start a development server (auto-reload on file changes at http://localhost:5000\n# You can also execute ./run.sh\nuvicorn optimade.server.main:app --reload --port 5000\n\n# View auto-generated docs\nopen http://localhost:5000/docs\n# View Open API Schema\nopen http://localhost:5000/openapi.json\n
When developing, you can run both the server and an index meta-database server at the same time (from two separate terminals). Running the following:
./run.sh index\n# or\nuvicorn optimade.server.main_index:app --reload --port 5001\n
will run the index meta-database server at http://localhost:5001/v1.
"},{"location":"INSTALL/#testing-specific-backends","title":"Testing specific backends","text":"In order to run the test suite for a specific backend, the OPTIMADE_DATABASE_BACKEND
environment variable (or config option) can be set to one of 'mongodb'
, 'mongomock'
or 'elastic'
(see ServerConfig.database_backend
). Tests for the two \"real\" database backends, MongoDB and Elasticsearch, require a writable, temporary database to be accessible.
The easiest way to deploy these databases and run the tests is with Docker, as shown below. Docker installation instructions will depend on your system; on Linux, the docker
commands below may need to be prepended with sudo
, depending on your distribution. These commands should be run from a local optimade-python-tools directory.
The following command starts a local Elasticsearch v7 instance, runs the test suite, then stops and deletes the containers (required as the tests insert some data):
docker run -d --name elasticsearch_test -p 9200:9200 -p 9300:9300 -e \"discovery.type=single-node\" -e \"xpack.security.enabled=false\" elasticsearch:7.17.7 \\\n&& sleep 20 \\\n&& OPTIMADE_DATABASE_BACKEND=\"elastic\" py.test; \\\ndocker container stop elasticsearch_test; docker container rm elasticsearch_test\n
The following command starts a local MongoDB instance, runs the test suite, then stops and deletes the containers:
docker run -d --name mongo_test -p 27017:27017 -d mongo:4.4.6 \\\n&& OPTIMADE_DATABASE_BACKEND=\"mongodb\" py.test; \\\ndocker container stop mongo_test; docker container rm mongo_test\n
"},{"location":"INSTALL/#container-image","title":"Container image","text":""},{"location":"INSTALL/#retrieve-the-image","title":"Retrieve the image","text":"The optimade
container image is available from the GitHub Container registry. To pull the latest version using Docker run the following:
docker pull ghcr.io/materials-consortia/optimade:latest\n
Note
The tag, :latest
, can be left out, as the latest
version will be pulled by default.
If you'd like to pull a specific version, this can be done by replacing latest
in the command above with the version of choice, e.g., 0.17.1
. To see which versions are available, please go here.
You can also install the develop
version. This is an image built from the latest commit on the main
branch and should never be used for production.
When starting a container from the image there are a few choices. It is possible to run either a standard OPTIMADE server, or an index meta-database server from this image. Note, these servers can be run in separate containers at the same time. The key is setting the environment variable MAIN
.
main
Standard OPTIMADE server. main_index
Index meta-database OPTIMADE server. Using Docker, the following command will run a container from the image:
# rm will remove container when it exits.\n# detach will run the server in the background.\n# publish will run the server from the host port 8080.\n# name will give the container a handy name for referencing later.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n ghcr.io/materials-consortia/optimade:latest\n
The server should now be available at localhost:8080.
"},{"location":"all_models/","title":"OPTIMADE Data Models","text":"This page provides documentation for the optimade.models
submodule, where all the OPTIMADE (and JSON:API)-defined data models are located.
For example, the three OPTIMADE entry types, structures
, references
and links
, are defined primarily through the corresponding attribute models:
StructureResourceAttributes
ReferenceResourceAttributes
LinksResourceAttributes
As well as validating data types when creating instances of these models, this package defines several OPTIMADE-specific validators that ensure consistency between fields (e.g., the value of nsites
matches the number of positions provided in cartesian_site_positions
).
ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"all_models/#optimade.models.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"all_models/#optimade.models.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"all_models/#optimade.models.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"all_models/#optimade.models.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"all_models/#optimade.models.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"all_models/#optimade.models.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"all_models/#optimade.models.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"all_models/#optimade.models.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"all_models/#optimade.models.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"all_models/#optimade.models.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"all_models/#optimade.models.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"all_models/#optimade.models.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"all_models/#optimade.models.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"all_models/#optimade.models.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"all_models/#optimade.models.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"all_models/#optimade.models.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"all_models/#optimade.models.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"all_models/#optimade.models.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"all_models/#optimade.models.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"all_models/#optimade.models.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"all_models/#optimade.models.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo","title":"baseinfo
","text":""},{"location":"all_models/#optimade.models.baseinfo.VERSIONED_BASE_URL_PATTERN","title":"VERSIONED_BASE_URL_PATTERN = '^.+/v[0-1](\\\\.[0-9]+)*/?$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.baseinfo.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries","title":"entries
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.entries.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.entries.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.entries.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship","title":"ReferenceRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class ReferenceRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"references\"]] = \"references\"\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.ReferenceRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship","title":"StructureRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class StructureRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"structures\"]] = \"structures\"\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.StructureRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.StructureRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship","title":"TypedRelationship
","text":" Bases: Relationship
optimade/models/entries.py
class TypedRelationship(Relationship):\n _req_type: ClassVar[str]\n\n @field_validator(\"data\", mode=\"after\")\n @classmethod\n def check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n ) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.entries.TypedRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.entries.TypedRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"all_models/#optimade.models.index_metadb","title":"index_metadb
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.index_metadb.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.index_metadb.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi","title":"jsonapi
","text":"This module should reproduce JSON API v1.0 https://jsonapi.org/format/1.0/
"},{"location":"all_models/#optimade.models.jsonapi.JsonLinkType","title":"JsonLinkType = Union[AnyUrl, Link]
module-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"all_models/#optimade.models.jsonapi.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error","title":"Error
","text":" Bases: BaseModel
An error response
Source code inoptimade/models/jsonapi.py
class Error(BaseModel):\n \"\"\"An error response\"\"\"\n\n id: Annotated[\n Optional[str],\n StrictField(\n description=\"A unique identifier for this particular occurrence of the problem.\",\n ),\n ] = None\n links: Annotated[\n Optional[ErrorLinks], StrictField(description=\"A links object storing about\")\n ] = None\n status: Annotated[\n Optional[Annotated[str, BeforeValidator(str)]],\n StrictField(\n description=\"the HTTP status code applicable to this problem, expressed as a string value.\",\n ),\n ] = None\n code: Annotated[\n Optional[str],\n StrictField(\n description=\"an application-specific error code, expressed as a string value.\",\n ),\n ] = None\n title: Annotated[\n Optional[str],\n StrictField(\n description=\"A short, human-readable summary of the problem. \"\n \"It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.\",\n ),\n ] = None\n detail: Annotated[\n Optional[str],\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ] = None\n source: Annotated[\n Optional[ErrorSource],\n StrictField(\n description=\"An object containing references to the source of the error\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the error.\",\n ),\n ] = None\n\n def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.jsonapi.Error.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.detail","title":"detail: Annotated[Optional[str], StrictField(description='A human-readable explanation specific to this occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Error.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"all_models/#optimade.models.jsonapi.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship","title":"Relationship
","text":" Bases: BaseModel
Representation references from the resource object in which it's defined to other resource objects.
Source code inoptimade/models/jsonapi.py
class Relationship(BaseModel):\n \"\"\"Representation references from the resource object in which it's defined to other resource objects.\"\"\"\n\n links: Annotated[\n Optional[RelationshipLinks],\n StrictField(\n description=\"a links object containing at least one of the following: self, related\",\n ),\n ] = None\n data: Annotated[\n Optional[Union[BaseResource, list[BaseResource]]],\n StrictField(description=\"Resource linkage\"),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object that contains non-standard meta-information about the relationship.\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationship.data","title":"data: Annotated[Optional[Union[BaseResource, list[BaseResource]]], StrictField(description='Resource linkage')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.jsonapi.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"all_models/#optimade.models.jsonapi.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.jsonapi.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.jsonapi.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.jsonapi.resource_json_schema_extra","title":"resource_json_schema_extra(schema, model)
","text":"Ensure id
and type
are the first two entries in the list required properties.
This requires that id
and type
are the first model fields defined for all sub-models of BaseResource
.
optimade/models/jsonapi.py
def resource_json_schema_extra(\n schema: dict[str, Any], model: type[\"BaseResource\"]\n) -> None:\n \"\"\"Ensure `id` and `type` are the first two entries in the list required properties.\n\n Note:\n This _requires_ that `id` and `type` are the _first_ model fields defined\n for all sub-models of `BaseResource`.\n\n \"\"\"\n if \"id\" not in schema.get(\"required\", []):\n schema[\"required\"] = [\"id\"] + schema.get(\"required\", [])\n if \"type\" not in schema.get(\"required\", []):\n required = []\n for field in schema.get(\"required\", []):\n required.append(field)\n if field == \"id\":\n # To make sure the property order match the listed properties,\n # this ensures \"type\" is added immediately after \"id\".\n required.append(\"type\")\n schema[\"required\"] = required\n
"},{"location":"all_models/#optimade.models.links","title":"links
","text":""},{"location":"all_models/#optimade.models.links.Aggregate","title":"Aggregate
","text":" Bases: Enum
Enumeration of aggregate values
Source code inoptimade/models/links.py
class Aggregate(Enum):\n \"\"\"Enumeration of aggregate values\"\"\"\n\n OK = \"ok\"\n TEST = \"test\"\n STAGING = \"staging\"\n NO = \"no\"\n
"},{"location":"all_models/#optimade.models.links.Aggregate.NO","title":"NO = 'no'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.OK","title":"OK = 'ok'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.STAGING","title":"STAGING = 'staging'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.Aggregate.TEST","title":"TEST = 'test'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType","title":"LinkType
","text":" Bases: Enum
Enumeration of link_type values
Source code inoptimade/models/links.py
class LinkType(Enum):\n \"\"\"Enumeration of link_type values\"\"\"\n\n CHILD = \"child\"\n ROOT = \"root\"\n EXTERNAL = \"external\"\n PROVIDERS = \"providers\"\n
"},{"location":"all_models/#optimade.models.links.LinkType.CHILD","title":"CHILD = 'child'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.EXTERNAL","title":"EXTERNAL = 'external'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.PROVIDERS","title":"PROVIDERS = 'providers'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinkType.ROOT","title":"ROOT = 'root'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.links.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"all_models/#optimade.models.links.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.links.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json","title":"optimade_json
","text":"Modified JSON API v1.0 for OPTIMADE API
"},{"location":"all_models/#optimade.models.optimade_json.ValidIdentifier","title":"ValidIdentifier = Annotated[str, Field(pattern=IDENTIFIER_REGEX)]
module-attribute
","text":"A type that constrains strings to valid OPTIMADE identifiers (e.g., property names, ID strings).
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"all_models/#optimade.models.optimade_json.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"all_models/#optimade.models.optimade_json.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.optimade_json.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.optimade_json.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.optimade_json.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.optimade_json.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"all_models/#optimade.models.optimade_json.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"all_models/#optimade.models.optimade_json.warnings_json_schema_extra","title":"warnings_json_schema_extra(schema, model)
","text":"Update OpenAPI JSON schema model for Warning
.
type
is in the list required properties and in the correct place.status
property. This property is not allowed for Warning
, nor is it a part of the OPTIMADE definition of the Warning
object.Since type
is the last model field defined, it will simply be appended.
optimade/models/optimade_json.py
def warnings_json_schema_extra(schema: dict[str, Any], model: type[\"Warnings\"]) -> None:\n \"\"\"Update OpenAPI JSON schema model for `Warning`.\n\n * Ensure `type` is in the list required properties and in the correct place.\n * Remove `status` property.\n This property is not allowed for `Warning`, nor is it a part of the OPTIMADE\n definition of the `Warning` object.\n\n Note:\n Since `type` is the _last_ model field defined, it will simply be appended.\n\n \"\"\"\n if \"required\" in schema:\n if \"type\" not in schema[\"required\"]:\n schema[\"required\"].append(\"type\")\n else:\n schema[\"required\"] = [\"type\"]\n schema.get(\"properties\", {}).pop(\"status\", None)\n
"},{"location":"all_models/#optimade.models.references","title":"references
","text":""},{"location":"all_models/#optimade.models.references.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.references.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.references.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.responses","title":"responses
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"all_models/#optimade.models.responses.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.responses.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"all_models/#optimade.models.responses.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"all_models/#optimade.models.structures","title":"structures
","text":""},{"location":"all_models/#optimade.models.structures.CORRELATED_STRUCTURE_FIELDS","title":"CORRELATED_STRUCTURE_FIELDS = ({'dimension_types', 'nperiodic_dimensions'}, {'cartesian_site_positions', 'species_at_sites'}, {'nsites', 'cartesian_site_positions'}, {'species_at_sites', 'species'})
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.EPS","title":"EPS = 2 ** -23
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Vector3D_unknown","title":"Vector3D_unknown = Annotated[list[Optional[Annotated[float, BeforeValidator(float)]]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"all_models/#optimade.models.structures.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"all_models/#optimade.models.structures.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"all_models/#optimade.models.structures.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"all_models/#optimade.models.structures.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"all_models/#optimade.models.structures.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"all_models/#optimade.models.types","title":"types
","text":""},{"location":"all_models/#optimade.models.types.AnnotatedType","title":"AnnotatedType = type(ChemicalSymbol)
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.ChemicalSymbol","title":"ChemicalSymbol = Annotated[str, Field(pattern=EXTENDED_CHEMICAL_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.ElementSymbol","title":"ElementSymbol = Annotated[str, Field(pattern=ELEMENT_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.NoneType","title":"NoneType = type(None)
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.OptionalType","title":"OptionalType = type(Optional[str])
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.SemanticVersion","title":"SemanticVersion = Annotated[str, Field(pattern=SEMVER_PATTERN, examples=['0.10.1', '1.0.0-rc.2', '1.2.3-rc.5+develop'])]
module-attribute
","text":""},{"location":"all_models/#optimade.models.types.UnionType","title":"UnionType = type(Union[str, int])
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils","title":"utils
","text":""},{"location":"all_models/#optimade.models.utils.ANONYMOUS_ELEMENTS","title":"ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150))
module-attribute
","text":"Returns the first 150 values of the anonymous element generator.
"},{"location":"all_models/#optimade.models.utils.ATOMIC_NUMBERS","title":"ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.CHEMICAL_FORMULA_REGEXP","title":"CHEMICAL_FORMULA_REGEXP = '(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\\\d+)?)+$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.ELEMENT_SYMBOLS_PATTERN","title":"ELEMENT_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.EXTENDED_CHEMICAL_SYMBOLS_PATTERN","title":"EXTENDED_CHEMICAL_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS + EXTRA_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.IDENTIFIER_REGEX","title":"IDENTIFIER_REGEX = '^[a-z_][a-z_0-9]+$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_KEYS","title":"OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable']
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_PREFIX","title":"OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SEMVER_PATTERN","title":"SEMVER_PATTERN = '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$'
module-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"all_models/#optimade.models.utils.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"all_models/#optimade.models.utils.OptimadeField","title":"OptimadeField(default=PydanticUndefined, *, support=None, queryable=None, unit=None, **kwargs)
","text":"A wrapper around pydantic.Field
that adds OPTIMADE-specific field paramters queryable
, support
and unit
, indicating the corresponding support level in the specification and the physical unit of the field.
Parameters:
Name Type Description Defaultsupport
Optional[Union[str, SupportLevel]]
The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.
None
queryable
Optional[Union[str, SupportLevel]]
The support level corresponding to the queryablility of this field.
None
unit
Optional[str]
A string describing the unit of the field.
None
Returns:
Type DescriptionAny
The pydantic field with extra validation provided by StrictField
.
optimade/models/utils.py
def OptimadeField(\n default: \"Any\" = PydanticUndefined,\n *,\n support: Optional[Union[str, SupportLevel]] = None,\n queryable: Optional[Union[str, SupportLevel]] = None,\n unit: Optional[str] = None,\n **kwargs,\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that adds OPTIMADE-specific\n field paramters `queryable`, `support` and `unit`, indicating\n the corresponding support level in the specification and the\n physical unit of the field.\n\n Arguments:\n support: The support level of the field itself, i.e. whether the field\n can be null or omitted by an implementation.\n queryable: The support level corresponding to the queryablility\n of this field.\n unit: A string describing the unit of the field.\n\n Returns:\n The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].\n\n \"\"\"\n\n # Collect non-null keyword arguments to add to the Field schema\n if unit is not None:\n kwargs[\"unit\"] = unit\n\n if queryable is not None:\n if isinstance(queryable, str):\n queryable = SupportLevel(queryable.lower())\n kwargs[\"queryable\"] = queryable\n\n if support is not None:\n if isinstance(support, str):\n support = SupportLevel(support.lower())\n kwargs[\"support\"] = support\n\n return StrictField(default, **kwargs)\n
"},{"location":"all_models/#optimade.models.utils.StrictField","title":"StrictField(default=PydanticUndefined, *, description=None, **kwargs)
","text":"A wrapper around pydantic.Field
that does the following:
pydantic.Field
, except those used elsewhere to modify the schema in-place, e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g. \"unit\", \"queryable\" and \"sortable\".Parameters:
Name Type Description Defaultdefault
Any
The only non-keyword argument allowed for Field.
PydanticUndefined
description
Optional[str]
The description of the Field
; if this is not specified then a UserWarning
will be emitted.
None
**kwargs
Any
Extra keyword arguments to be passed to Field
.
{}
Raises:
Type DescriptionRuntimeError
If **kwargs
contains a key not found in the function signature of Field
, or in the extensions used by models in this package (see above).
Returns:
Type DescriptionAny
The pydantic Field
.
optimade/models/utils.py
def StrictField(\n default: \"Any\" = PydanticUndefined,\n *,\n description: Optional[str] = None,\n **kwargs: \"Any\",\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that does the following:\n\n - Forbids any \"extra\" keys that would be passed to `pydantic.Field`,\n except those used elsewhere to modify the schema in-place,\n e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g.\n \"unit\", \"queryable\" and \"sortable\".\n - Emits a warning when no description is provided.\n\n Arguments:\n default: The only non-keyword argument allowed for Field.\n description: The description of the `Field`; if this is not\n specified then a `UserWarning` will be emitted.\n **kwargs: Extra keyword arguments to be passed to `Field`.\n\n Raises:\n RuntimeError: If `**kwargs` contains a key not found in the\n function signature of `Field`, or in the extensions used\n by models in this package (see above).\n\n Returns:\n The pydantic `Field`.\n\n \"\"\"\n allowed_schema_and_field_keys = [\"pattern\"]\n\n allowed_keys = [\n \"pattern\",\n \"uniqueItems\",\n ] + OPTIMADE_SCHEMA_EXTENSION_KEYS\n _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]\n\n if _banned:\n raise RuntimeError(\n f\"Not creating StrictField({default!r}, **{kwargs!r}) with \"\n f\"forbidden keywords {_banned}.\"\n )\n\n # Handle description\n if description is None:\n warnings.warn(\n f\"No description provided for StrictField specified by {default!r}, \"\n f\"**{kwargs!r}.\"\n )\n else:\n kwargs[\"description\"] = description\n\n # OPTIMADE schema extensions\n json_schema_extra: dict[str, Any] = kwargs.pop(\"json_schema_extra\", {})\n\n # Go through all JSON Schema keys and add them to the json_schema_extra.\n for key in allowed_keys:\n if key not in kwargs:\n continue\n\n # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.\n schema_key = (\n f\"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}\"\n if key in OPTIMADE_SCHEMA_EXTENSION_KEYS\n else key\n )\n\n for key_variant in (key, schema_key):\n if key_variant in json_schema_extra:\n if json_schema_extra.pop(key_variant) != kwargs[key]:\n raise RuntimeError(\n f\"Conflicting values for {key} in json_schema_extra and kwargs.\"\n )\n\n json_schema_extra[schema_key] = (\n kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)\n )\n\n kwargs[\"json_schema_extra\"] = json_schema_extra\n\n return Field(default, **kwargs)\n
"},{"location":"all_models/#optimade.models.utils.anonymize_formula","title":"anonymize_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and returns the OPTIMADE chemical_formula_anonymous
representation, i.e., a reduced chemical formula comprising of element symbols drawn from A, B, C... ordered from largest proportion to smallest.
Returns:
Type Descriptionstr
The anonymous chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def anonymize_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n returns the OPTIMADE `chemical_formula_anonymous` representation, i.e., a reduced chemical formula comprising of element symbols\n drawn from A, B, C... ordered from largest proportion to smallest.\n\n Returns:\n The anonymous chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=False, anonymize=True)\n
"},{"location":"all_models/#optimade.models.utils.anonymous_element_generator","title":"anonymous_element_generator()
","text":"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.
Source code inoptimade/models/utils.py
def anonymous_element_generator() -> \"Generator[str, None, None]\":\n \"\"\"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.\"\"\"\n from string import ascii_lowercase\n\n for size in itertools.count(1):\n for tuple_strings in itertools.product(ascii_lowercase, repeat=size):\n list_strings = list(tuple_strings)\n list_strings[0] = list_strings[0].upper()\n yield \"\".join(list_strings)\n
"},{"location":"all_models/#optimade.models.utils.reduce_formula","title":"reduce_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.
Returns:
Type Descriptionstr
The reduced chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def reduce_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.\n\n Returns:\n The reduced chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=True, anonymize=False)\n
"},{"location":"configuration/","title":"Configuration","text":"Since the server implementation is built with FastAPI, which uses pydantic, the configuration is based on pydantic's Setting management. This way of handling configuration options supports various different approaches to configure the server. We recommend either or a combination of the following:
OPTIMADE_CONFIG_FILE
environment variable.OPTIMADE_
or optimade_
.ServerConfig
object with the desired settings directly.The main way of configuring the OPTIMADE server is by creating a configuration JSON file.
An example of one that works with the example implementation can be found in optimade_config.json
:
{\n \"debug\": false,\n \"default_db\": \"test_server\",\n \"base_url\": \"http://localhost:5000\",\n \"implementation\": {\n \"name\": \"Example implementation\",\n \"source_url\": \"https://github.com/Materials-Consortia/optimade-python-tools\",\n \"issue_tracker\": \"https://github.com/Materials-Consortia/optimade-python-tools/issues\",\n \"homepage\": \"https://optimade.org/optimade-python-tools\",\n \"maintainer\": {\"email\": \"dev@optimade.org\"}\n },\n \"provider\": {\n \"name\": \"Example provider\",\n \"description\": \"Provider used for examples, not to be assigned to a real database\",\n \"prefix\": \"exmpl\",\n \"homepage\": \"https://example.com\"\n },\n \"index_base_url\": \"http://localhost:5001\",\n \"provider_fields\": {\n \"structures\": [\n \"band_gap\",\n {\"name\": \"chemsys\", \"type\": \"string\", \"description\": \"A string representing the chemical system in an ordered fashion\"}\n ]\n },\n \"aliases\": {\n \"structures\": {\n \"id\": \"task_id\",\n \"immutable_id\": \"_id\",\n \"chemical_formula_descriptive\": \"pretty_formula\",\n \"chemical_formula_reduced\": \"pretty_formula\",\n \"chemical_formula_anonymous\": \"formula_anonymous\"\n }\n },\n \"length_aliases\": {\n \"structures\": {\n \"chemsys\": \"nelements\"\n }\n }\n}\n
"},{"location":"configuration/#environment-variables","title":"Environment variables","text":"In order for the implementation to know where your configuration JSON file is located, you can set an environment variable OPTIMADE_CONFIG_FILE
with either the value of the absolute path to the configuration file or the relative path to the file from the current working directory of where the server is run.
This variable is actually an extension of the configuration option config_file
. By default, the server will try to load a JSON file called .optimade.json
located in your home folder (or equivalent).
Here the generally recognized environment variable prefix becomes evident, namely OPTIMADE_
or optimade_
. Hence, you can set (or overwrite) any configuration option from the server's defaults or a value read from the configuration JSON by setting an environment variable named OPTIMADE_<configuration_option>
.
One can extend the current list of configuration options by sub-classing ServerConfig
and adding configuration options as attributes with values of Field
(pydantic.field
). Any attribute type will be validated through pydantic
as is the case for all of the regular configuration options.
This is useful for, e.g., custom database backends, if one wants to utilize the general server configuration setup implemented in optimade
to declare specific database information. It can also be useful if one wishes to extend and build upon the general optimade
server with new endpoints and routes.
Remember to instantiate an instance of the sub-class, which can be imported and used in your application.
"},{"location":"configuration/#list-of-configuration-options","title":"List of configuration options","text":"See config.py
for a complete list of configuration options.
The following configuration file represents the default values for all configuration options:
Default values for all configuration options{\n \"debug\": false,\n \"insert_test_data\": true,\n \"mongo_database\": \"optimade\",\n \"mongo_uri\": \"localhost:27017\",\n \"links_collection\": \"links\",\n \"references_collection\": \"references\",\n \"structures_collection\": \"structures\",\n \"page_limit\": 20,\n \"page_limit_max\": 500,\n \"default_db\": \"test_server\",\n \"base_url\": null,\n \"implementation\": {\n \"name\": \"OPTIMADE Python Tools\",\n \"version\": \"1.1.4\",\n \"source_url\": \"https://github.com/Materials-Consortia/optimade-python-tools\",\n \"maintainer\": {\"email\": \"dev@optimade.org\"}\n },\n \"index_base_url\": null,\n \"provider\": {\n \"name\": \"Example provider\",\n \"description\": \"Provider used for examples, not to be assigned to a real database\",\n \"prefix\": \"exmpl\",\n \"homepage\": \"https://example.com\"\n },\n \"provider_fields\": {},\n \"aliases\": {},\n \"length_aliases\": {},\n \"index_links_path\": \"./optimade/server/index_links.json\",\n \"log_level\": \"info\",\n \"log_dir\": \"/var/log/optimade/\",\n \"validate_query_parameters\": true,\n \"validate_api_response\": true\n}\n
"},{"location":"api_reference/exceptions/","title":"exceptions","text":""},{"location":"api_reference/exceptions/#optimade.exceptions.BadRequest","title":"BadRequest
","text":" Bases: OptimadeHTTPException
400 Bad Request
Source code inoptimade/exceptions.py
class BadRequest(OptimadeHTTPException):\n \"\"\"400 Bad Request\"\"\"\n\n status_code: int = 400\n title: str = \"Bad Request\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.Forbidden","title":"Forbidden
","text":" Bases: OptimadeHTTPException
403 Forbidden
Source code inoptimade/exceptions.py
class Forbidden(OptimadeHTTPException):\n \"\"\"403 Forbidden\"\"\"\n\n status_code: int = 403\n title: str = \"Forbidden\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.InternalServerError","title":"InternalServerError
","text":" Bases: OptimadeHTTPException
500 Internal Server Error
Source code inoptimade/exceptions.py
class InternalServerError(OptimadeHTTPException):\n \"\"\"500 Internal Server Error\"\"\"\n\n status_code: int = 500\n title: str = \"Internal Server Error\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.NotFound","title":"NotFound
","text":" Bases: OptimadeHTTPException
404 Not Found
Source code inoptimade/exceptions.py
class NotFound(OptimadeHTTPException):\n \"\"\"404 Not Found\"\"\"\n\n status_code: int = 404\n title: str = \"Not Found\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.NotImplementedResponse","title":"NotImplementedResponse
","text":" Bases: OptimadeHTTPException
501 Not Implemented
Source code inoptimade/exceptions.py
class NotImplementedResponse(OptimadeHTTPException):\n \"\"\"501 Not Implemented\"\"\"\n\n status_code: int = 501\n title: str = \"Not Implemented\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.OptimadeHTTPException","title":"OptimadeHTTPException
","text":" Bases: Exception
, ABC
This abstract class can be subclassed to define HTTP responses with the desired status codes, and detailed error strings to represent in the JSON:API error response.
This class closely follows the starlette.HTTPException
without requiring it as a dependency, so that such errors can also be raised from within client code.
Attributes:
Name Type Descriptionstatus_code
int
The HTTP status code accompanying this exception.
title
str
A descriptive title for this exception.
detail
Optional[str]
An optional string containing the details of the error.
Source code inoptimade/exceptions.py
class OptimadeHTTPException(Exception, ABC):\n \"\"\"This abstract class can be subclassed to define\n HTTP responses with the desired status codes, and\n detailed error strings to represent in the JSON:API\n error response.\n\n This class closely follows the `starlette.HTTPException` without\n requiring it as a dependency, so that such errors can also be\n raised from within client code.\n\n Attributes:\n status_code: The HTTP status code accompanying this exception.\n title: A descriptive title for this exception.\n detail: An optional string containing the details of the error.\n\n \"\"\"\n\n status_code: int\n title: str\n detail: Optional[str] = None\n headers: Optional[dict[str, Any]] = None\n\n def __init__(\n self, detail: Optional[str] = None, headers: Optional[dict] = None\n ) -> None:\n if self.status_code is None:\n raise AttributeError(\n \"HTTPException class {self.__class__.__name__} is missing required `status_code` attribute.\"\n )\n self.detail = detail\n self.headers = headers\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else self.__repr__()\n\n def __repr__(self) -> str:\n class_name = self.__class__.__name__\n return f\"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.UnprocessableEntity","title":"UnprocessableEntity
","text":" Bases: OptimadeHTTPException
422 Unprocessable Entity
Source code inoptimade/exceptions.py
class UnprocessableEntity(OptimadeHTTPException):\n \"\"\"422 Unprocessable Entity\"\"\"\n\n status_code: int = 422\n title: str = \"Unprocessable Entity\"\n
"},{"location":"api_reference/exceptions/#optimade.exceptions.VersionNotSupported","title":"VersionNotSupported
","text":" Bases: OptimadeHTTPException
553 Version Not Supported
Source code inoptimade/exceptions.py
class VersionNotSupported(OptimadeHTTPException):\n \"\"\"553 Version Not Supported\"\"\"\n\n status_code: int = 553\n title: str = \"Version Not Supported\"\n
"},{"location":"api_reference/utils/","title":"utils","text":"This submodule implements some useful utilities for dealing with OPTIMADE providers that can be used in server or client code.
"},{"location":"api_reference/utils/#optimade.utils.get_all_databases","title":"get_all_databases(include_providers=None, exclude_providers=None, exclude_databases=None, progress=None, skip_ssl=False)
","text":"Iterate through all databases reported by registered OPTIMADE providers.
Parameters:
Name Type Description Defaultinclude_providers
Optional[Container[str]]
A set/container of provider IDs to include child databases for.
None
exclude_providers
Optional[Container[str]]
A set/container of provider IDs to exclude child databases for.
None
exclude_databases
Optional[Container[str]]
A set/container of specific database URLs to exclude.
None
Returns:
Type DescriptionIterable[str]
A generator of child database links that obey the given parameters.
Source code inoptimade/utils.py
def get_all_databases(\n include_providers: Optional[Container[str]] = None,\n exclude_providers: Optional[Container[str]] = None,\n exclude_databases: Optional[Container[str]] = None,\n progress: \"Optional[rich.Progress]\" = None,\n skip_ssl: bool = False,\n) -> Iterable[str]:\n \"\"\"Iterate through all databases reported by registered OPTIMADE providers.\n\n Parameters:\n include_providers: A set/container of provider IDs to include child databases for.\n exclude_providers: A set/container of provider IDs to exclude child databases for.\n exclude_databases: A set/container of specific database URLs to exclude.\n\n Returns:\n A generator of child database links that obey the given parameters.\n\n \"\"\"\n if progress is not None:\n _task = progress.add_task(\n description=\"Retrieving all databases from registered OPTIMADE providers...\",\n total=None,\n )\n else:\n progress = contextlib.nullcontext()\n progress.print = lambda _: None # type: ignore[attr-defined]\n progress.advance = lambda *_: None # type: ignore[attr-defined]\n _task = None\n\n with progress:\n for provider in get_providers():\n if exclude_providers and provider[\"id\"] in exclude_providers:\n continue\n if include_providers and provider[\"id\"] not in include_providers:\n continue\n\n try:\n links = get_child_database_links(provider, skip_ssl=skip_ssl)\n for link in links:\n if link.attributes.base_url:\n if (\n exclude_databases\n and link.attributes.base_url in exclude_databases\n ):\n continue\n yield str(link.attributes.base_url)\n if links and progress is not None:\n progress.advance(_task, 1)\n progress.print(\n f\"Retrieved databases from [bold green]{provider['id']}[/bold green]\"\n )\n except RuntimeError as exc:\n if progress is not None:\n progress.print(\n f\"Unable to retrieve databases from [bold red]{provider['id']}[/bold red]: {exc}\",\n )\n pass\n
"},{"location":"api_reference/utils/#optimade.utils.get_child_database_links","title":"get_child_database_links(provider, obey_aggregate=True, headers=None, skip_ssl=False)
","text":"For a provider, return a list of available child databases.
Parameters:
Name Type Description Defaultprovider
LinksResource
The links entry for the provider.
requiredobey_aggregate
bool
Whether to only return links that allow aggregation.
True
headers
Optional[dict]
Additional HTTP headers to pass to the provider.
None
Returns:
Type Descriptionlist[LinksResource]
A list of the valid links entries from this provider that
list[LinksResource]
have link_type
\"child\"
.
Raises:
Type DescriptionRuntimeError
If the provider's index meta-database is down, invalid, or the request otherwise fails.
Source code inoptimade/utils.py
def get_child_database_links(\n provider: LinksResource,\n obey_aggregate: bool = True,\n headers: Optional[dict] = None,\n skip_ssl: bool = False,\n) -> list[LinksResource]:\n \"\"\"For a provider, return a list of available child databases.\n\n Arguments:\n provider: The links entry for the provider.\n obey_aggregate: Whether to only return links that allow\n aggregation.\n headers: Additional HTTP headers to pass to the provider.\n\n Returns:\n A list of the valid links entries from this provider that\n have `link_type` `\"child\"`.\n\n Raises:\n RuntimeError: If the provider's index meta-database is down,\n invalid, or the request otherwise fails.\n\n \"\"\"\n import requests\n\n from optimade.models.links import Aggregate, LinkType\n\n base_url = provider.pop(\"base_url\")\n if base_url is None:\n raise RuntimeError(f\"Provider {provider['id']} provides no base URL.\")\n\n links_endp = base_url + \"/v1/links\"\n try:\n links = requests.get(links_endp, timeout=10, headers=headers)\n except SSLError as exc:\n if skip_ssl:\n links = requests.get(links_endp, timeout=10, headers=headers, verify=False)\n else:\n raise RuntimeError(\n f\"SSL error when connecting to provider {provider['id']}. Use `skip_ssl` to ignore.\"\n ) from exc\n except (requests.ConnectionError, requests.Timeout) as exc:\n raise RuntimeError(f\"Unable to connect to provider {provider['id']}\") from exc\n\n if links.status_code != 200:\n raise RuntimeError(\n f\"Invalid response from {links_endp} for provider {provider['id']}: {links.content!r}.\"\n )\n\n try:\n links_resources = links.json().get(\"data\", [])\n return_links = []\n for link in links_resources:\n link = LinksResource(**link)\n if (\n link.attributes.link_type == LinkType.CHILD\n and link.attributes.base_url is not None\n and (not obey_aggregate or link.attributes.aggregate == Aggregate.OK)\n ):\n return_links.append(link)\n\n return return_links\n\n except (ValidationError, json.JSONDecodeError) as exc:\n raise RuntimeError(\n f\"Did not understand response from {provider['id']}: {links.content!r}, {exc}\"\n )\n
"},{"location":"api_reference/utils/#optimade.utils.get_providers","title":"get_providers(add_mongo_id=False)
","text":"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).
Fallback order if providers.optimade.org is not available:
providers
' list of providers./links
-endpoint.Parameters:
Name Type Description Defaultadd_mongo_id
bool
Whether to populate the _id
field of the provider with MongoDB ObjectID.
False
Returns:
Type Descriptionlist
List of raw JSON-decoded providers including MongoDB object IDs.
Source code inoptimade/utils.py
def get_providers(add_mongo_id: bool = False) -> list:\n \"\"\"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).\n\n Fallback order if providers.optimade.org is not available:\n\n 1. Try Materials-Consortia/providers on GitHub.\n 2. Try submodule `providers`' list of providers.\n 3. Log warning that providers list from Materials-Consortia is not included in the\n `/links`-endpoint.\n\n Arguments:\n add_mongo_id: Whether to populate the `_id` field of the provider with MongoDB\n ObjectID.\n\n Returns:\n List of raw JSON-decoded providers including MongoDB object IDs.\n\n \"\"\"\n import json\n\n import requests\n\n for provider_list_url in PROVIDER_LIST_URLS:\n try:\n providers = requests.get(provider_list_url, timeout=10).json()\n except (\n requests.exceptions.ConnectionError,\n requests.exceptions.ConnectTimeout,\n json.JSONDecodeError,\n requests.exceptions.SSLError,\n ):\n pass\n else:\n break\n else:\n try:\n from optimade.server.data import providers # type: ignore\n except ImportError:\n from optimade.server.logger import LOGGER\n\n LOGGER.warning(\n \"\"\"Could not retrieve a list of providers!\n\n Tried the following resources:\n\n{}\n The list of providers will not be included in the `/links`-endpoint.\n\"\"\".format(\"\".join([f\" * {_}\\n\" for _ in PROVIDER_LIST_URLS]))\n )\n return []\n\n providers_list = []\n for provider in providers.get(\"data\", []):\n # Remove/skip \"exmpl\"\n if provider[\"id\"] == \"exmpl\":\n continue\n\n provider.update(provider.pop(\"attributes\", {}))\n\n # Add MongoDB ObjectId\n if add_mongo_id:\n provider[\"_id\"] = {\n \"$oid\": mongo_id_for_database(provider[\"id\"], provider[\"type\"])\n }\n\n providers_list.append(provider)\n\n return providers_list\n
"},{"location":"api_reference/utils/#optimade.utils.insert_from_jsonl","title":"insert_from_jsonl(jsonl_path)
","text":"Insert OPTIMADE JSON lines data into the database.
Parameters:
Name Type Description Defaultjsonl_path
Path
Path to the JSON lines file.
required Source code inoptimade/utils.py
def insert_from_jsonl(jsonl_path: Path) -> None:\n \"\"\"Insert OPTIMADE JSON lines data into the database.\n\n Arguments:\n jsonl_path: Path to the JSON lines file.\n\n \"\"\"\n from collections import defaultdict\n\n import bson.json_util\n\n from optimade.server.routers import ENTRY_COLLECTIONS\n\n batch = defaultdict(list)\n batch_size: int = 1000\n\n # Attempt to treat path as absolute, otherwise join with root directory\n if not jsonl_path.is_file():\n _jsonl_path = Path(__file__).parent.joinpath(jsonl_path)\n if not _jsonl_path.is_file():\n raise FileNotFoundError(\n f\"Could not find file {jsonl_path} or {_jsonl_path}\"\n )\n jsonl_path = _jsonl_path\n\n with open(jsonl_path) as handle:\n header = handle.readline()\n header_jsonl = json.loads(header)\n assert header_jsonl.get(\n \"x-optimade\"\n ), \"No x-optimade header, not sure if this is a JSONL file\"\n\n for json_str in handle:\n try:\n entry = bson.json_util.loads(json_str)\n except json.JSONDecodeError:\n print(f\"Could not read entry as JSON: {json_str}\")\n print_exc()\n continue\n try:\n id = entry.get(\"id\", None)\n _type = entry.get(\"type\", None)\n if id is None or _type == \"info\":\n # assume this is an info endpoint for pre-1.2\n continue\n\n inp_data = entry[\"attributes\"]\n inp_data[\"id\"] = id\n # Append the data to the batch\n batch[_type].append(inp_data)\n except Exception as exc:\n print(f\"Error with entry {entry}: {exc}\")\n print_exc()\n continue\n\n if len(batch[_type]) >= batch_size:\n ENTRY_COLLECTIONS[_type].insert(batch[_type])\n batch[_type] = []\n\n # Insert any remaining data\n for entry_type in batch:\n ENTRY_COLLECTIONS[entry_type].insert(batch[entry_type])\n batch[entry_type] = []\n
"},{"location":"api_reference/utils/#optimade.utils.mongo_id_for_database","title":"mongo_id_for_database(database_id, database_type)
","text":"Produce a MongoDB ObjectId for a database
Source code inoptimade/utils.py
def mongo_id_for_database(database_id: str, database_type: str) -> str:\n \"\"\"Produce a MongoDB ObjectId for a database\"\"\"\n from bson.objectid import ObjectId\n\n oid = f\"{database_id}{database_type}\"\n if len(oid) > 12:\n oid = oid[:12]\n elif len(oid) < 12:\n oid = f\"{oid}{'0' * (12 - len(oid))}\"\n\n return str(ObjectId(oid.encode(\"UTF-8\")))\n
"},{"location":"api_reference/warnings/","title":"warnings","text":"This submodule implements the possible warnings that can be omitted by an OPTIMADE API.
"},{"location":"api_reference/warnings/#optimade.warnings.FieldValueNotRecognized","title":"FieldValueNotRecognized
","text":" Bases: OptimadeWarning
A field or value used in the request is not recognised by this implementation.
Source code inoptimade/warnings.py
class FieldValueNotRecognized(OptimadeWarning):\n \"\"\"A field or value used in the request is not recognised by this implementation.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.LocalOptimadeWarning","title":"LocalOptimadeWarning
","text":" Bases: OptimadeWarning
A warning that is specific to a local implementation of the OPTIMADE API and should not appear in the server log or response.
Source code inoptimade/warnings.py
class LocalOptimadeWarning(OptimadeWarning):\n \"\"\"A warning that is specific to a local implementation of the OPTIMADE API\n and should not appear in the server log or response.\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.MissingExpectedField","title":"MissingExpectedField
","text":" Bases: LocalOptimadeWarning
A field was provided with a null value when a related field was provided with a value.
Source code inoptimade/warnings.py
class MissingExpectedField(LocalOptimadeWarning):\n \"\"\"A field was provided with a null value when a related field was provided\n with a value.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.OptimadeWarning","title":"OptimadeWarning
","text":" Bases: Warning
Base Warning for the optimade
package
optimade/warnings.py
class OptimadeWarning(Warning):\n \"\"\"Base Warning for the `optimade` package\"\"\"\n\n def __init__(\n self, detail: Optional[str] = None, title: Optional[str] = None, *args\n ) -> None:\n detail = detail if detail else self.__doc__\n super().__init__(detail, *args)\n self.detail = detail\n self.title = title if title else self.__class__.__name__\n\n def __repr__(self) -> str:\n attrs = {\"detail\": self.detail, \"title\": self.title}\n return \"<{:s}({:s})>\".format(\n self.__class__.__name__,\n \" \".join(\n [\n f\"{attr}={value!r}\"\n for attr, value in attrs.items()\n if value is not None\n ]\n ),\n )\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else \"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.QueryParamNotUsed","title":"QueryParamNotUsed
","text":" Bases: OptimadeWarning
A query parameter is not used in this request.
Source code inoptimade/warnings.py
class QueryParamNotUsed(OptimadeWarning):\n \"\"\"A query parameter is not used in this request.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.TimestampNotRFCCompliant","title":"TimestampNotRFCCompliant
","text":" Bases: OptimadeWarning
A timestamp has been used in a filter that contains microseconds and is thus not RFC 3339 compliant. This may cause undefined behaviour in the query results.
Source code inoptimade/warnings.py
class TimestampNotRFCCompliant(OptimadeWarning):\n \"\"\"A timestamp has been used in a filter that contains microseconds and is thus not\n RFC 3339 compliant. This may cause undefined behaviour in the query results.\n\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.TooManyValues","title":"TooManyValues
","text":" Bases: OptimadeWarning
A field or query parameter has too many values to be handled by this implementation.
Source code inoptimade/warnings.py
class TooManyValues(OptimadeWarning):\n \"\"\"A field or query parameter has too many values to be handled by this implementation.\"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.UnknownProviderProperty","title":"UnknownProviderProperty
","text":" Bases: OptimadeWarning
A provider-specific property has been requested via response_fields
or as in a filter
that is not recognised by this implementation.
optimade/warnings.py
class UnknownProviderProperty(OptimadeWarning):\n \"\"\"A provider-specific property has been requested via `response_fields` or as in a `filter` that is not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/warnings/#optimade.warnings.UnknownProviderQueryParameter","title":"UnknownProviderQueryParameter
","text":" Bases: OptimadeWarning
A provider-specific query parameter has been requested in the query with a prefix not recognised by this implementation.
Source code inoptimade/warnings.py
class UnknownProviderQueryParameter(OptimadeWarning):\n \"\"\"A provider-specific query parameter has been requested in the query with a prefix not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/adapters/base/","title":"base","text":"The base for all adapters.
An entry resource adapter is a tool to wrap OPTIMADE JSON-deserialized Python dictionaries in the relevant pydantic model for the particular resource.
This means data resources in an OPTIMADE REST API response can be converted to valid Python types written specifically for them. One can then use the standard pydantic functionality on the wrapped objects, reasoning about the embedded hierarchical types as well as retrieve default values for properties not supplied by the raw API response resource.
Furthermore, the entry resource adapter allows conversion between the entry resource and any implemented equivalent data structure.
See Reference
and Structure
to find out what the entry resources can be converted to for ReferenceResource
s and StructureResource
s, respectively.
EntryAdapter
","text":"Base class for lazy resource entry adapters.
Attributes:
Name Type DescriptionENTRY_RESOURCE
type[EntryResource]
Entry resource to store entry as.
_type_converters
dict[str, Callable]
Dictionary of valid conversion types for entry.
_type_ingesters
dict[str, Callable]
Dictionary of valid ingestion types mapped to ingestion functions.
_type_ingesters_by_type
dict[str, type]
Dictionary mapping the keys of _type_ingesters
to data types that can be ingested.
as_<_type_converters>
dict[str, type]
Convert entry to a type listed in _type_converters
.
from_<_type_converters>
dict[str, type]
Convert an external type to the corresponding OPTIMADE model.
Source code inoptimade/adapters/base.py
class EntryAdapter:\n \"\"\"\n Base class for lazy resource entry adapters.\n\n Attributes:\n ENTRY_RESOURCE: Entry resource to store entry as.\n _type_converters: Dictionary of valid conversion types for entry.\n _type_ingesters: Dictionary of valid ingestion types mapped to ingestion functions.\n _type_ingesters_by_type: Dictionary mapping the keys of `_type_ingesters` to data\n types that can be ingested.\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n from_<_type_converters>: Convert an external type to the corresponding OPTIMADE model.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[EntryResource] = EntryResource\n _type_converters: dict[str, Callable] = {}\n _type_ingesters: dict[str, Callable] = {}\n _type_ingesters_by_type: dict[str, type] = {}\n\n def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n\n @property\n def entry(self) -> EntryResource:\n \"\"\"Get OPTIMADE entry.\n\n Returns:\n The entry resource.\n\n \"\"\"\n return self._entry\n\n def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n\n @classmethod\n def ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n\n @classmethod\n def from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n\n @staticmethod\n def _get_model_attributes(\n starting_instances: Union[tuple[BaseModel, ...], list[BaseModel]], name: str\n ) -> Any:\n \"\"\"Helper method for retrieving the OPTIMADE model's attribute, supporting \".\"-nested attributes\"\"\"\n for res in starting_instances:\n nested_attributes = name.split(\".\")\n for nested_attribute in nested_attributes:\n if nested_attribute in getattr(res, \"model_fields\", {}):\n res = getattr(res, nested_attribute)\n else:\n res = None\n break\n if res is not None:\n return res\n raise AttributeError\n\n def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/base/#optimade.adapters.base.EntryAdapter.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/exceptions/","title":"exceptions","text":""},{"location":"api_reference/adapters/exceptions/#optimade.adapters.exceptions.ConversionError","title":"ConversionError
","text":" Bases: Exception
Could not convert entry to format
Source code inoptimade/adapters/exceptions.py
class ConversionError(Exception):\n \"\"\"Could not convert entry to format\"\"\"\n
"},{"location":"api_reference/adapters/logger/","title":"logger","text":"Logger for optimade.adapters
"},{"location":"api_reference/adapters/warnings/","title":"warnings","text":""},{"location":"api_reference/adapters/warnings/#optimade.adapters.warnings.AdapterPackageNotFound","title":"AdapterPackageNotFound
","text":" Bases: OptimadeWarning
The package for an adapter cannot be found.
Source code inoptimade/adapters/warnings.py
class AdapterPackageNotFound(OptimadeWarning):\n \"\"\"The package for an adapter cannot be found.\"\"\"\n
"},{"location":"api_reference/adapters/warnings/#optimade.adapters.warnings.ConversionWarning","title":"ConversionWarning
","text":" Bases: OptimadeWarning
A non-critical error/fallback/choice happened during conversion of an entry to format.
Source code inoptimade/adapters/warnings.py
class ConversionWarning(OptimadeWarning):\n \"\"\"A non-critical error/fallback/choice happened during conversion of an entry to format.\"\"\"\n
"},{"location":"api_reference/adapters/references/adapter/","title":"adapter","text":""},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference","title":"Reference
","text":" Bases: EntryAdapter
Lazy reference resource converter.
Go to EntryAdapter
to see the full list of methods and properties.
Attributes:
Name Type DescriptionENTRY_RESOURCE
ReferenceResource
This adapter stores entry resources as ReferenceResource
s.
_type_converters
Dict[str, Callable]
Dictionary of valid conversion types for entry.
There are currently no available types.
as_<_type_converters>
Dict[str, Callable]
Convert entry to a type listed in _type_converters
.
optimade/adapters/references/adapter.py
class Reference(EntryAdapter):\n \"\"\"\n Lazy reference resource converter.\n\n Go to [`EntryAdapter`][optimade.adapters.base.EntryAdapter] to see the full list of methods\n and properties.\n\n Attributes:\n ENTRY_RESOURCE (ReferenceResource): This adapter stores entry resources as\n [`ReferenceResource`][optimade.models.references.ReferenceResource]s.\n _type_converters (Dict[str, Callable]): Dictionary of valid conversion types for entry.\n\n There are currently no available types.\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[ReferenceResource] = ReferenceResource\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/references/adapter/#optimade.adapters.references.adapter.Reference.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/structures/adapter/","title":"adapter","text":""},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure","title":"Structure
","text":" Bases: EntryAdapter
Lazy structure resource converter.
Go to EntryAdapter
to see the full list of methods and properties.
Attributes:
Name Type DescriptionENTRY_RESOURCE
type[StructureResource]
This adapter stores entry resources as StructureResource
s.
_type_converters
dict[str, Callable]
Dictionary of valid conversion types for entry.
Currently available types:
aiida_structuredata
ase
cif
pdb
pdbx_mmcif
pymatgen
jarvis
_type_ingesters
dict[str, Callable]
Dictionary of valid ingesters.
as_<_type_converters>
dict[str, Callable]
Convert entry to a type listed in _type_converters
.
from_<_type_converters>
dict[str, Callable]
Convert an external type to an OPTIMADE StructureResourceAttributes
model.
optimade/adapters/structures/adapter.py
class Structure(EntryAdapter):\n \"\"\"\n Lazy structure resource converter.\n\n Go to [`EntryAdapter`][optimade.adapters.base.EntryAdapter] to see the full list of methods\n and properties.\n\n Attributes:\n ENTRY_RESOURCE: This adapter stores entry resources as\n [`StructureResource`][optimade.models.structures.StructureResource]s.\n _type_converters: Dictionary of valid conversion types for entry.\n\n Currently available types:\n\n - `aiida_structuredata`\n - `ase`\n - `cif`\n - `pdb`\n - `pdbx_mmcif`\n - `pymatgen`\n - `jarvis`\n\n _type_ingesters: Dictionary of valid ingesters.\n\n as_<_type_converters>: Convert entry to a type listed in `_type_converters`.\n from_<_type_converters>: Convert an external type to an OPTIMADE\n [`StructureResourceAttributes`][optimade.models.structures.StructureResourceAttributes]\n model.\n\n \"\"\"\n\n ENTRY_RESOURCE: type[StructureResource] = StructureResource\n _type_converters: dict[str, Callable] = {\n \"aiida_structuredata\": get_aiida_structure_data,\n \"ase\": get_ase_atoms,\n \"cif\": get_cif,\n \"pdb\": get_pdb,\n \"pdbx_mmcif\": get_pdbx_mmcif,\n \"pymatgen\": get_pymatgen,\n \"jarvis\": get_jarvis_atoms,\n }\n\n _type_ingesters: dict[str, Callable] = {\n \"pymatgen\": from_pymatgen,\n \"ase\": from_ase_atoms,\n }\n\n _type_ingesters_by_type: dict[str, type] = {\n \"pymatgen\": PymatgenStructure,\n \"ase\": ASEAtoms,\n }\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.entry","title":"entry: EntryResource
property
","text":"Get OPTIMADE entry.
Returns:
Type DescriptionEntryResource
The entry resource.
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.__getattr__","title":"__getattr__(name)
","text":"Get converted entry or attribute from OPTIMADE entry.
Support any level of \".\"-nested OPTIMADE ENTRY_RESOURCE
attributes, e.g., attributes.species
for StuctureResource
.
All nested attributes must individually be subclasses of pydantic.BaseModel
, i.e., one can not access nested attributes in lists by passing a \".\"-nested name
to this method, e.g., attributes.species.name
or attributes.species[0].name
will not work for variable name
.
Order:
as_<_type_converters key>
.ENTRY_RESOURCE
(nested) attribute.ENTRY_RESOURCE.attributes
(nested) attribute.AttributeError
.Parameters:
Name Type Description Defaultname
str
Requested attribute.
requiredRaises:
Type DescriptionAttributeError
If the requested attribute is not recognized. See above for the description of the order in which an attribute is tested for validity.
Source code inoptimade/adapters/base.py
def __getattr__(self, name: str) -> Any:\n \"\"\"Get converted entry or attribute from OPTIMADE entry.\n\n Support any level of \".\"-nested OPTIMADE `ENTRY_RESOURCE` attributes, e.g.,\n `attributes.species` for [`StuctureResource`][optimade.models.structures.StructureResource].\n\n Note:\n All nested attributes must individually be subclasses of `pydantic.BaseModel`,\n i.e., one can not access nested attributes in lists by passing a \".\"-nested `name` to this method,\n e.g., `attributes.species.name` or `attributes.species[0].name` will not work for variable `name`.\n\n Order:\n\n - Try to return converted entry if using `as_<_type_converters key>`.\n - Try to return OPTIMADE `ENTRY_RESOURCE` (nested) attribute.\n - Try to return OPTIMADE `ENTRY_RESOURCE.attributes` (nested) attribute.\n - Raise `AttributeError`.\n\n Parameters:\n name (str): Requested attribute.\n\n Raises:\n AttributeError: If the requested attribute is not recognized.\n See above for the description of the order in which an attribute is tested for validity.\n\n \"\"\"\n # as_<entry_type>\n if name.startswith(\"as_\"):\n entry_type = \"_\".join(name.split(\"_\")[1:])\n return self.convert(entry_type)\n\n # Try returning ENTRY_RESOURCE attribute\n try:\n res = self._get_model_attributes((self.entry, self.entry.attributes), name)\n except AttributeError:\n pass\n else:\n return res\n\n # Non-valid attribute\n _entry_resource_name = re.match(\n r\"(<class ')([a-zA-Z_]+\\.)*([a-zA-Z_]+)('>)\", str(self.ENTRY_RESOURCE)\n )\n entry_resource_name = (\n _entry_resource_name.group(3)\n if _entry_resource_name is not None\n else \"UNKNOWN RESOURCE\"\n )\n raise AttributeError(\n f\"Unknown attribute: {name}\\n\"\n \"If you want to get a converted entry as <entry_type> use `as_<entry_type>`, \"\n f\"where `<entry_type>` is one of {tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\\n\"\n f\"Otherwise, you can try to retrieve an OPTIMADE {entry_resource_name} attribute or property.\"\n )\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.__init__","title":"__init__(entry)
","text":"Parameters:
Name Type Description Defaultentry
dict
A JSON OPTIMADE single resource entry.
required Source code inoptimade/adapters/base.py
def __init__(self, entry: dict[str, Any]) -> None:\n \"\"\"\n Parameters:\n entry (dict): A JSON OPTIMADE single resource entry.\n \"\"\"\n self._converted: dict[str, Any] = {}\n\n self._entry = self.ENTRY_RESOURCE(**entry)\n\n # Note that these return also the default values for otherwise non-provided properties.\n self._common_converters = {\n # Return JSON serialized string, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeljson\n \"json\": self.entry.model_dump_json,\n # Return Python dict, see https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict\n \"dict\": self.entry.model_dump,\n }\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.convert","title":"convert(format)
","text":"Convert OPTIMADE entry to desired format.
Parameters:
Name Type Description Defaultformat
str
Type or format to which the entry should be converted.
requiredRaises:
Type DescriptionAttributeError
If format
can not be found in _type_converters
or _common_converters
.
Returns:
Type DescriptionAny
The converted entry according to the desired format or type.
Source code inoptimade/adapters/base.py
def convert(self, format: str) -> Any:\n \"\"\"Convert OPTIMADE entry to desired format.\n\n Parameters:\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_converters` or `_common_converters`.\n\n Returns:\n The converted entry according to the desired format or type.\n\n \"\"\"\n if (\n format not in self._type_converters\n and format not in self._common_converters\n ):\n raise AttributeError(\n f\"Non-valid entry type to convert to: {format}\\nValid entry types: \"\n f\"{tuple(self._type_converters.keys()) + tuple(self._common_converters.keys())}\"\n )\n\n if self._converted.get(format, None) is None:\n if format in self._type_converters:\n self._converted[format] = self._type_converters[format](self.entry)\n else:\n self._converted[format] = self._common_converters[format]()\n\n return self._converted[format]\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.from_url","title":"from_url(url)
classmethod
","text":"Convert OPTIMADE URL into the target entry type.
Parameters:
Name Type Description Defaulturl
str
The OPTIMADE URL to convert.
requiredReturns:
Type DescriptionAny
The converted URL.
Source code inoptimade/adapters/base.py
@classmethod\ndef from_url(cls, url: str) -> Any:\n \"\"\"Convert OPTIMADE URL into the target entry type.\n\n Parameters:\n url (str): The OPTIMADE URL to convert.\n\n Returns:\n The converted URL.\n\n \"\"\"\n import requests\n\n response = requests.get(url, timeout=100)\n if response.status_code != 200:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url} returned {response.status_code}\"\n )\n\n try:\n json_response = response.json()\n\n except JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not retrieve OPTIMADE entry from URL {url}: did not contain valid JSON response.\"\n ) from exc\n\n data: dict = json_response.get(\"data\", {})\n if isinstance(data, list):\n raise RuntimeError(f\"returned a list of {len(data)} entries.\")\n\n return cls(data)\n
"},{"location":"api_reference/adapters/structures/adapter/#optimade.adapters.structures.adapter.Structure.ingest_from","title":"ingest_from(data, format=None)
classmethod
","text":"Convert desired format to OPTIMADE format.
Parameters:
Name Type Description Defaultdata
Any
The data to convert.
requiredformat
str
Type or format to which the entry should be converted.
None
Raises:
Type DescriptionAttributeError
If format
can not be found in _type_ingesters
.
Returns:
Type DescriptionAny
The ingested Structure.
Source code inoptimade/adapters/base.py
@classmethod\ndef ingest_from(cls, data: Any, format: Optional[str] = None) -> Any:\n \"\"\"Convert desired format to OPTIMADE format.\n\n Parameters:\n data (Any): The data to convert.\n format (str): Type or format to which the entry should be converted.\n\n Raises:\n AttributeError: If `format` can not be found in `_type_ingesters`.\n\n Returns:\n The ingested Structure.\n\n \"\"\"\n\n if format is None:\n for key, instance_type in cls._type_ingesters_by_type.items():\n if isinstance(data, instance_type):\n format = key\n break\n\n else:\n raise AttributeError(\n f\"Non entry type to data of type {type(data)} from.\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n if format not in cls._type_ingesters:\n raise AttributeError(\n f\"Non-valid entry type to ingest from: {format}\\n\"\n f\"Valid entry types: {tuple(cls._type_ingesters.keys())}\"\n )\n\n return cls(\n {\n \"attributes\": cls._type_ingesters[format](data).model_dump(),\n \"id\": \"\",\n \"type\": \"structures\",\n }\n )\n
"},{"location":"api_reference/adapters/structures/aiida/","title":"aiida","text":"Convert an OPTIMADE structure, in the format of StructureResource
to an AiiDA StructureData
Node.
For more information on the AiiDA code see their website.
This conversion function relies on the aiida-core
package.
get_aiida_structure_data(optimade_structure)
","text":"Get AiiDA StructureData
from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionStructureData
AiiDA StructureData
Node.
optimade/adapters/structures/aiida.py
def get_aiida_structure_data(optimade_structure: OptimadeStructure) -> StructureData:\n \"\"\"Get AiiDA `StructureData` from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n AiiDA `StructureData` Node.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"StructureData\")):\n warn(AIIDA_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Convert null/None values to float(\"nan\")\n lattice_vectors, adjust_cell = pad_cell(attributes.lattice_vectors) # type: ignore[arg-type]\n structure = StructureData(cell=lattice_vectors)\n\n # If species not provided, infer data from species_at_sites\n species: Optional[list[OptimadeStructureSpecies]] = attributes.species\n if not species:\n species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type]\n\n # Add Kinds\n for kind in species:\n symbols = []\n concentration = []\n mass = 0.0\n for index, chemical_symbol in enumerate(kind.chemical_symbols):\n # NOTE: The non-chemical element identifier \"X\" is identical to how AiiDA handles this,\n # so it will be treated the same as any other true chemical identifier.\n if chemical_symbol == \"vacancy\":\n # Skip. This is how AiiDA handles vacancies;\n # to not include them, while keeping the concentration in a site less than 1.\n continue\n else:\n symbols.append(chemical_symbol)\n concentration.append(kind.concentration[index])\n\n # AiiDA needs a definition for the mass, and for it to be > 0\n # mass is OPTIONAL for OPTIMADE structures\n if kind.mass:\n mass += kind.concentration[index] * kind.mass[index]\n\n if not mass:\n warn(\n f\"No mass defined for <species(name={kind.name!r})>, will default to setting mass to 1.0.\",\n ConversionWarning,\n )\n\n structure.append_kind(\n Kind(\n symbols=symbols, weights=concentration, mass=mass or 1.0, name=kind.name\n )\n )\n\n # Add Sites\n for index in range(attributes.nsites): # type: ignore[arg-type]\n # range() to ensure 1-to-1 between kind and site\n structure.append_site(\n Site(\n kind_name=attributes.species_at_sites[index], # type: ignore[index]\n position=attributes.cartesian_site_positions[index], # type: ignore[index]\n )\n )\n\n if adjust_cell:\n structure._adjust_default_cell(\n pbc=[bool(dim.value) for dim in attributes.dimension_types] # type: ignore[union-attr]\n )\n\n return structure\n
"},{"location":"api_reference/adapters/structures/ase/","title":"ase","text":"Convert an OPTIMADE structure, in the format of StructureResource
to an ASE Atoms
object.
This conversion function relies on the ASE code.
For more information on the ASE code see their documentation.
"},{"location":"api_reference/adapters/structures/ase/#optimade.adapters.structures.ase.from_ase_atoms","title":"from_ase_atoms(atoms)
","text":"Convert an ASE Atoms
object into an OPTIMADE StructureResourceAttributes
model.
Parameters:
Name Type Description Defaultatoms
Atoms
The ASE Atoms
object to convert.
Returns:
Type DescriptionStructureResourceAttributes
An OPTIMADE StructureResourceAttributes
model, which can be converted to a raw Python dictionary with .model_dump()
or to JSON with .model_dump_json()
.
optimade/adapters/structures/ase.py
def from_ase_atoms(atoms: Atoms) -> StructureResourceAttributes:\n \"\"\"Convert an ASE `Atoms` object into an OPTIMADE `StructureResourceAttributes` model.\n\n Parameters:\n atoms: The ASE `Atoms` object to convert.\n\n Returns:\n An OPTIMADE `StructureResourceAttributes` model, which can be converted to a raw Python\n dictionary with `.model_dump()` or to JSON with `.model_dump_json()`.\n\n \"\"\"\n if not isinstance(atoms, Atoms):\n raise RuntimeError(\n f\"Cannot convert type {type(atoms)} into an OPTIMADE `StructureResourceAttributes` model.\"\n )\n\n attributes = {}\n attributes[\"cartesian_site_positions\"] = atoms.positions.tolist()\n attributes[\"lattice_vectors\"] = atoms.cell.tolist()\n attributes[\"species_at_sites\"] = atoms.get_chemical_symbols()\n attributes[\"elements_ratios\"] = elements_ratios_from_species_at_sites(\n attributes[\"species_at_sites\"]\n )\n attributes[\"species\"] = species_from_species_at_sites(\n attributes[\"species_at_sites\"]\n )\n attributes[\"dimension_types\"] = [int(_) for _ in atoms.pbc.tolist()]\n attributes[\"nperiodic_dimensions\"] = sum(attributes[\"dimension_types\"])\n attributes[\"nelements\"] = len(attributes[\"species\"])\n attributes[\"elements\"] = sorted([_.name for _ in attributes[\"species\"]])\n attributes[\"nsites\"] = len(attributes[\"species_at_sites\"])\n\n attributes[\"chemical_formula_descriptive\"] = atoms.get_chemical_formula()\n attributes[\"chemical_formula_reduced\"] = reduce_formula(\n atoms.get_chemical_formula()\n )\n attributes[\"chemical_formula_anonymous\"] = anonymize_formula(\n attributes[\"chemical_formula_reduced\"],\n )\n attributes[\"last_modified\"] = None\n attributes[\"immutable_id\"] = None\n attributes[\"structure_features\"] = []\n\n for key in atoms.info:\n optimade_key = key.lower()\n if not key.startswith(f\"_{EXTRA_FIELD_PREFIX}\"):\n optimade_key = f\"_{EXTRA_FIELD_PREFIX}_{optimade_key}\"\n attributes[optimade_key] = atoms.info[key]\n\n return StructureResourceAttributes(**attributes)\n
"},{"location":"api_reference/adapters/structures/ase/#optimade.adapters.structures.ase.get_ase_atoms","title":"get_ase_atoms(optimade_structure)
","text":"Get ASE Atoms
from OPTIMADE structure.
Cannot handle partial occupancies (this includes vacancies).
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionAtoms
ASE Atoms
object.
optimade/adapters/structures/ase.py
def get_ase_atoms(optimade_structure: OptimadeStructure) -> Atoms:\n \"\"\"Get ASE `Atoms` from OPTIMADE structure.\n\n Caution:\n Cannot handle partial occupancies (this includes vacancies).\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n ASE `Atoms` object.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Atoms\")):\n warn(ASE_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Cannot handle partial occupancies\n if StructureFeatures.DISORDER in attributes.structure_features:\n raise ConversionError(\n \"ASE cannot handle structures with partial occupancies, sorry.\"\n )\n\n species = attributes.species\n # If species is missing, infer data from species_at_sites\n if not species:\n species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type]\n\n optimade_species: dict[str, OptimadeStructureSpecies] = {_.name: _ for _ in species}\n\n # Since we've made sure there are no species with more than 1 chemical symbol,\n # asking for index 0 will always work.\n if \"X\" in [specie.chemical_symbols[0] for specie in optimade_species.values()]:\n raise ConversionError(\n \"ASE cannot handle structures with unknown ('X') chemical symbols, sorry.\"\n )\n\n atoms = []\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = attributes.cartesian_site_positions[site_number] # type: ignore[index]\n\n current_species = optimade_species[species_name]\n\n # Argument above about chemical symbols also holds here\n mass = None\n if current_species.mass:\n mass = current_species.mass[0]\n\n atoms.append(\n Atom(symbol=current_species.chemical_symbols[0], position=site, mass=mass)\n )\n\n info = {}\n for key in attributes.model_dump().keys():\n if key.startswith(\"_\"):\n ase_key = key\n if key.startswith(f\"_{EXTRA_FIELD_PREFIX}_\"):\n ase_key = \"\".join(key.split(f\"_{EXTRA_FIELD_PREFIX}_\")[1:])\n info[ase_key] = getattr(attributes, key)\n\n return Atoms(\n symbols=atoms,\n cell=attributes.lattice_vectors,\n pbc=attributes.dimension_types,\n info=info if info else None,\n )\n
"},{"location":"api_reference/adapters/structures/cif/","title":"cif","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a CIF file (Crystallographic Information File).
For more information on the CIF file format, see the official documentation.
NoteThis conversion function is inspired heavily by the similar conversion function in the ASE library.
See here for the original ASE code.
For more information on the ASE library, see their documentation.
This conversion function relies on the NumPy library.
"},{"location":"api_reference/adapters/structures/cif/#optimade.adapters.structures.cif.get_cif","title":"get_cif(optimade_structure)
","text":"Get CIF file as string from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type Descriptionstr
The CIF file as a single Python str
object.
optimade/adapters/structures/cif.py
def get_cif(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Get CIF file as string from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n The CIF file as a single Python `str` object.\n\n \"\"\"\n # NumPy is needed for calculations\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cif = \"\"\"#\n# Created from an OPTIMADE structure.\n#\n# See https://www.optimade.org and/or\n# https://github.com/Materials-Consortia/OPTIMADE for more information.\n#\n\"\"\"\n\n cif += f\"data_{optimade_structure.id}\\n\\n\"\n\n attributes = optimade_structure.attributes\n\n # Do this only if there's three non-zero lattice vectors\n # NOTE: This also negates handling of lattice_vectors with null/None values\n if valid_lattice_vector(attributes.lattice_vectors): # type:ignore[arg-type]\n a_vector, b_vector, c_vector, alpha, beta, gamma = cell_to_cellpar(\n attributes.lattice_vectors # type: ignore[arg-type]\n )\n\n cif += (\n f\"_cell_length_a {a_vector:g}\\n\"\n f\"_cell_length_b {b_vector:g}\\n\"\n f\"_cell_length_c {c_vector:g}\\n\"\n f\"_cell_angle_alpha {alpha:g}\\n\"\n f\"_cell_angle_beta {beta:g}\\n\"\n f\"_cell_angle_gamma {gamma:g}\\n\\n\"\n )\n cif += (\n \"_symmetry_space_group_name_H-M 'P 1'\\n\"\n \"_symmetry_int_tables_number 1\\n\\n\"\n \"loop_\\n\"\n \" _symmetry_equiv_pos_as_xyz\\n\"\n \" 'x, y, z'\\n\\n\"\n )\n\n # Since some structure viewers are having issues with cartesian coordinates,\n # we calculate the fractional coordinates if this is a 3D structure and we have all the necessary information.\n if not hasattr(attributes, \"fractional_site_positions\"):\n attributes.fractional_site_positions = fractional_coordinates(\n cell=attributes.lattice_vectors, # type:ignore[arg-type]\n cartesian_positions=attributes.cartesian_site_positions, # type:ignore[arg-type]\n )\n\n # NOTE: This is otherwise a bit ahead of its time, since this OPTIMADE property is part of an open PR.\n # See https://github.com/Materials-Consortia/OPTIMADE/pull/206\n coord_type = (\n \"fract\" if hasattr(attributes, \"fractional_site_positions\") else \"Cartn\"\n )\n\n cif += (\n \"loop_\\n\"\n \" _atom_site_type_symbol\\n\" # species.chemical_symbols\n \" _atom_site_label\\n\" # species.name + unique int\n \" _atom_site_occupancy\\n\" # species.concentration\n f\" _atom_site_{coord_type}_x\\n\" # cartesian_site_positions\n f\" _atom_site_{coord_type}_y\\n\" # cartesian_site_positions\n f\" _atom_site_{coord_type}_z\\n\" # cartesian_site_positions\n \" _atom_site_thermal_displace_type\\n\" # Set to 'Biso'\n \" _atom_site_B_iso_or_equiv\\n\" # Set to 1.0:f\n )\n\n if coord_type == \"fract\":\n sites = attributes.fractional_site_positions\n else:\n sites = attributes.cartesian_site_positions\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type: ignore[union-attr]\n }\n\n symbol_occurences: dict[str, int] = {}\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n if symbol in symbol_occurences:\n symbol_occurences[symbol] += 1\n else:\n symbol_occurences[symbol] = 1\n label = f\"{symbol}{symbol_occurences[symbol]}\"\n\n cif += (\n f\" {symbol} {label} {current_species.concentration[index]:6.4f} {site[0]:8.5f} \"\n f\"{site[1]:8.5f} {site[2]:8.5f} {'Biso':4} {'1.000':6}\\n\"\n )\n\n return cif\n
"},{"location":"api_reference/adapters/structures/jarvis/","title":"jarvis","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a JARVIS Atoms
object.
For more information on the NIST-JARVIS repository, see their website.
This conversion function relies on the jarvis-tools package.
Contributing author
This conversion function was contributed by Kamal Choudhary (@knc6).
"},{"location":"api_reference/adapters/structures/jarvis/#optimade.adapters.structures.jarvis.get_jarvis_atoms","title":"get_jarvis_atoms(optimade_structure)
","text":"Get jarvis Atoms
from OPTIMADE structure.
Cannot handle partial occupancies.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionAtoms
A jarvis Atoms
object.
optimade/adapters/structures/jarvis.py
def get_jarvis_atoms(optimade_structure: OptimadeStructure) -> Atoms:\n \"\"\"Get jarvis `Atoms` from OPTIMADE structure.\n\n Caution:\n Cannot handle partial occupancies.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A jarvis `Atoms` object.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Atoms\")):\n warn(JARVIS_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n attributes = optimade_structure.attributes\n\n # Cannot handle partial occupancies\n if StructureFeatures.DISORDER in attributes.structure_features:\n raise ConversionError(\n \"jarvis-tools cannot handle structures with partial occupancies.\"\n )\n\n return Atoms(\n lattice_mat=attributes.lattice_vectors,\n elements=[specie.name for specie in attributes.species], # type: ignore[union-attr]\n coords=attributes.cartesian_site_positions,\n cartesian=True,\n )\n
"},{"location":"api_reference/adapters/structures/proteindatabank/","title":"proteindatabank","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a PDB file or PDBx/mmCIF file (Protein Data Bank).
For more information on the file formats, see this FAQ page from the wwPDB website.
NoteThese conversion functions are inspired heavily by the similar conversion functions in the ASE library.
See here (PDB) and here (PDBx/mmCIF) for the original ASE code.
For more information on the ASE library, see their documentation.
These conversion functions both rely on the NumPy library.
WarningCurrently, the PDBx/mmCIF conversion function is not parsing as a complete PDBx/mmCIF file.
"},{"location":"api_reference/adapters/structures/proteindatabank/#optimade.adapters.structures.proteindatabank.get_pdb","title":"get_pdb(optimade_structure)
","text":"Write Protein Data Bank (PDB) structure in the old PDB format from OPTIMADE structure.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type Descriptionstr
A PDB file as a single Python str
object.
optimade/adapters/structures/proteindatabank.py
def get_pdb(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Write Protein Data Bank (PDB) structure in the old PDB format from OPTIMADE structure.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A PDB file as a single Python `str` object.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n pdb = \"\"\n\n attributes = optimade_structure.attributes\n\n rotation = None\n if valid_lattice_vector(attributes.lattice_vectors): # type: ignore[arg-type]\n currentcell = np.asarray(attributes.lattice_vectors)\n cellpar = cell_to_cellpar(currentcell)\n exportedcell = cellpar_to_cell(cellpar)\n rotation = np.linalg.solve(currentcell, exportedcell)\n # Setting Z-value = 1 and using P1 since we have all atoms defined explicitly\n Z = 1\n spacegroup = \"P 1\"\n pdb += (\n f\"CRYST1{cellpar[0]:9.3f}{cellpar[1]:9.3f}{cellpar[2]:8.3f}\"\n f\"{cellpar[3]:7.2f}{cellpar[4]:7.2f}{cellpar[5]:7.2f} {spacegroup:11s}{Z:4d}\\n\"\n )\n\n for i, vector in enumerate(scaled_cell(currentcell)):\n pdb += f\"SCALE{i + 1} {vector[0]:10.6f}{vector[1]:10.6f}{vector[2]:10.6f} {0:10.5f}\\n\"\n\n # There is a limit of 5 digit numbers in this field.\n pdb_maxnum = 100000\n bfactor = 1.0\n\n pdb += \"MODEL 1\\n\"\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type:ignore[union-attr]\n }\n\n sites = np.asarray(attributes.cartesian_site_positions)\n if rotation is not None:\n sites = sites.dot(rotation)\n\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n label = species_name\n if len(current_species.chemical_symbols) > 1:\n if (\n \"vacancy\" in current_species.chemical_symbols\n and len(current_species.chemical_symbols) == 2\n ):\n pass\n else:\n label = f\"{symbol}{index + 1}\"\n\n pdb += (\n f\"ATOM {site_number % pdb_maxnum:5d} {label:4} MOL 1 \"\n f\"{site[0]:8.3f}{site[1]:8.3f}{site[2]:8.3f}\"\n f\"{current_species.concentration[index]:6.2f}\"\n f\"{bfactor:6.2f} {symbol.upper():2} \\n\"\n )\n pdb += \"ENDMDL\\n\"\n\n return pdb\n
"},{"location":"api_reference/adapters/structures/proteindatabank/#optimade.adapters.structures.proteindatabank.get_pdbx_mmcif","title":"get_pdbx_mmcif(optimade_structure)
","text":"Write Protein Data Bank (PDB) structure in the PDBx/mmCIF format from OPTIMADE structure.
WarningThe result of this function can currently not be parsed as a complete PDBx/mmCIF file.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
required ReturnA modern PDBx/mmCIF file as a single Python str
object.
optimade/adapters/structures/proteindatabank.py
def get_pdbx_mmcif(\n optimade_structure: OptimadeStructure,\n) -> str:\n \"\"\"Write Protein Data Bank (PDB) structure in the PDBx/mmCIF format from OPTIMADE structure.\n\n Warning:\n The result of this function can currently not be parsed as a complete PDBx/mmCIF file.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Return:\n A modern PDBx/mmCIF file as a single Python `str` object.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cif = \"\"\"#\n# Created from an OPTIMADE structure.\n#\n# See https://www.optimade.org and/or\n# https://github.com/Materials-Consortia/OPTIMADE for more information.\n#\n# CIF 2.0 format, specifically mmCIF (PDBx).\n# See http://mmcif.wwpdb.org for more information.\n#\n\"\"\"\n\n entry_id = f\"{optimade_structure.type}{optimade_structure.id}\"\n cif += f\"data_{entry_id}\\n_entry.id {entry_id}\\n#\\n\"\n\n attributes = optimade_structure.attributes\n\n # Do this only if there's three non-zero lattice vectors\n if valid_lattice_vector(attributes.lattice_vectors): # type: ignore[arg-type]\n a_vector, b_vector, c_vector, alpha, beta, gamma = cell_to_cellpar(\n attributes.lattice_vectors # type: ignore[arg-type]\n )\n\n cif += (\n f\"_cell.entry_id {entry_id}\\n\"\n f\"_cell.length_a {a_vector:g}\\n\"\n f\"_cell.length_b {b_vector:g}\\n\"\n f\"_cell.length_c {c_vector:g}\\n\"\n f\"_cell.angle_alpha {alpha:g}\\n\"\n f\"_cell.angle_beta {beta:g}\\n\"\n f\"_cell.angle_gamma {gamma:g}\\n\"\n \"_cell.Z_PDB 1\\n#\\n\"\n )\n cif += (\n f\"_symmetry.entry_id {entry_id}\\n\"\n \"_symmetry.space_group_name_H-M 'P 1'\\n\"\n \"_symmetry.Int_Tables_number 1\\n#\\n\"\n )\n\n # Since some structure viewers are having issues with cartesian coordinates,\n # we calculate the fractional coordinates if this is a 3D structure and we have all the necessary information.\n if not hasattr(attributes, \"fractional_site_positions\"):\n attributes.fractional_site_positions = fractional_coordinates(\n cell=attributes.lattice_vectors, # type: ignore[arg-type]\n cartesian_positions=attributes.cartesian_site_positions, # type: ignore[arg-type]\n )\n\n # NOTE: The following lines are perhaps needed to create a \"valid\" PDBx/mmCIF file.\n # However, at the same time, the information here is \"default\" and will for all structures \"at this moment in time\"\n # be the same. I.e., no information is gained by adding this now.\n # If it is found that they indeed are needed to create a \"valid\" PDBx/mmCIF file, they should be included in the output.\n # cif += (\n # \"loop_\\n\"\n # \"_struct_asym.id\\n\"\n # \"_struct_asym.entity_id\\n\"\n # \"A 1\\n#\\n\" # At this point, not using this feature.\n # )\n\n # cif += (\n # \"loop_\\n\"\n # \"_chem_comp.id\\n\"\n # \"X\\n#\\n\" # At this point, not using this feature.\n # )\n\n # cif += (\n # \"loop_\\n\"\n # \"_entity.id\\n\"\n # \"1\\n#\\n\" # At this point, not using this feature.\n # )\n\n # NOTE: This is otherwise a bit ahead of its time, since this OPTIMADE property is part of an open PR.\n # See https://github.com/Materials-Consortia/OPTIMADE/pull/206\n coord_type = (\n \"fract\" if hasattr(attributes, \"fractional_site_positions\") else \"Cartn\"\n )\n\n cif += (\n \"loop_\\n\"\n \"_atom_site.group_PDB\\n\" # Always \"ATOM\"\n \"_atom_site.id\\n\" # number (1-counting)\n \"_atom_site.type_symbol\\n\" # species.chemical_symbols\n \"_atom_site.label_atom_id\\n\" # species.checmical_symbols symbol + number\n # For these next keys, see the comment above.\n # \"_atom_site.label_asym_id\\n\" # Will be set to \"A\" _struct_asym.id above\n # \"_atom_site.label_comp_id\\n\" # Will be set to \"X\" _chem_comp.id above\n # \"_atom_site.label_entity_id\\n\" # Will be set to \"1\" _entity.id above\n # \"_atom_site.label_seq_id\\n\"\n \"_atom_site.occupancy\\n\" # species.concentration\n f\"_atom_site.{coord_type}_x\\n\" # cartesian_site_positions\n f\"_atom_site.{coord_type}_y\\n\" # cartesian_site_positions\n f\"_atom_site.{coord_type}_z\\n\" # cartesian_site_positions\n \"_atom_site.thermal_displace_type\\n\" # Set to 'Biso'\n \"_atom_site.B_iso_or_equiv\\n\" # Set to 1.0:f\n )\n\n if coord_type == \"fract\":\n sites = attributes.fractional_site_positions\n else:\n sites = attributes.cartesian_site_positions\n\n species: dict[str, OptimadeStructureSpecies] = {\n species.name: species\n for species in attributes.species # type: ignore[union-attr]\n }\n\n for site_number in range(attributes.nsites): # type: ignore[arg-type]\n species_name = attributes.species_at_sites[site_number] # type: ignore[index]\n site = sites[site_number]\n\n current_species = species[species_name]\n\n for index, symbol in enumerate(current_species.chemical_symbols):\n if symbol == \"vacancy\":\n continue\n\n label = f\"{species_name.upper()}{site_number + 1}\"\n if len(current_species.chemical_symbols) > 1:\n if (\n \"vacancy\" in current_species.chemical_symbols\n and len(current_species.chemical_symbols) == 2\n ):\n pass\n else:\n label = f\"{symbol.upper()}{index + 1}\"\n\n cif += (\n f\"ATOM {site_number + 1:5d} {symbol} {label:8} \"\n f\"{current_species.concentration[index]:6.4f} {site[0]:8.5f} \"\n f\"{site[1]:8.5f} {site[2]:8.5f} {'Biso':4} {'1.000':6}\\n\"\n )\n\n return cif\n
"},{"location":"api_reference/adapters/structures/pymatgen/","title":"pymatgen","text":"Convert an OPTIMADE structure, in the format of StructureResource
to a pymatgen Molecule
or Structure
object.
This conversion function relies on the pymatgen package.
For more information on the pymatgen code see their documentation.
"},{"location":"api_reference/adapters/structures/pymatgen/#optimade.adapters.structures.pymatgen.from_pymatgen","title":"from_pymatgen(pmg_structure)
","text":"Convert a pymatgen Structure
(3D) into an OPTIMADE StructureResourceAttributes
model.
Parameters:
Name Type Description Defaultpmg_structure
Structure
The pymatgen Structure
to convert.
Returns:
Type DescriptionStructureResourceAttributes
An OPTIMADE StructureResourceAttributes
model, which can be converted to a raw Python dictionary with .model_dump()
or to JSON with .model_dump_json()
.
optimade/adapters/structures/pymatgen.py
def from_pymatgen(pmg_structure: Structure) -> StructureResourceAttributes:\n \"\"\"Convert a pymatgen `Structure` (3D) into an OPTIMADE `StructureResourceAttributes` model.\n\n Parameters:\n pmg_structure: The pymatgen `Structure` to convert.\n\n Returns:\n An OPTIMADE `StructureResourceAttributes` model, which can be converted to a raw Python\n dictionary with `.model_dump()` or to JSON with `.model_dump_json()`.\n\n \"\"\"\n\n if not isinstance(pmg_structure, Structure):\n raise RuntimeError(\n f\"Cannot convert type {type(pmg_structure)} into an OPTIMADE `StructureResourceAttributes` model.\"\n )\n\n attributes = {}\n attributes[\"cartesian_site_positions\"] = pmg_structure.lattice.get_cartesian_coords(\n pmg_structure.frac_coords\n ).tolist()\n attributes[\"lattice_vectors\"] = pmg_structure.lattice.matrix.tolist()\n attributes[\"species_at_sites\"] = [_.symbol for _ in pmg_structure.species]\n attributes[\"species\"] = [\n {\"name\": _.symbol, \"chemical_symbols\": [_.symbol], \"concentration\": [1]}\n for _ in set(pmg_structure.composition.elements)\n ]\n attributes[\"dimension_types\"] = [int(_) for _ in pmg_structure.lattice.pbc]\n attributes[\"nperiodic_dimensions\"] = sum(attributes[\"dimension_types\"])\n attributes[\"nelements\"] = len(pmg_structure.composition.elements)\n attributes[\"chemical_formula_anonymous\"] = anonymize_formula(\n pmg_structure.composition.formula\n )\n attributes[\"elements\"] = sorted(\n [_.symbol for _ in pmg_structure.composition.elements]\n )\n attributes[\"chemical_formula_reduced\"] = reduce_formula(\n pmg_structure.composition.formula\n )\n attributes[\"chemical_formula_descriptive\"] = pmg_structure.composition.formula\n attributes[\"elements_ratios\"] = [\n pmg_structure.composition.get_atomic_fraction(e) for e in attributes[\"elements\"]\n ]\n attributes[\"nsites\"] = len(attributes[\"species_at_sites\"])\n\n attributes[\"last_modified\"] = None\n attributes[\"immutable_id\"] = None\n attributes[\"structure_features\"] = []\n\n return StructureResourceAttributes(**attributes)\n
"},{"location":"api_reference/adapters/structures/pymatgen/#optimade.adapters.structures.pymatgen.get_pymatgen","title":"get_pymatgen(optimade_structure)
","text":"Get pymatgen Structure
or Molecule
from OPTIMADE structure.
This function will return either a pymatgen Structure
or Molecule
based on the periodicity or periodic dimensionality of OPTIMADE structure.
For structures that are periodic in one or more dimensions, a pymatgen Structure
is returned when valid lattice_vectors are given. This means, if the any of the values in the dimension_types
attribute is 1
s or if nperiodic_dimesions
> 0.
Otherwise, a pymatgen Molecule
is returned.
Parameters:
Name Type Description Defaultoptimade_structure
StructureResource
OPTIMADE structure.
requiredReturns:
Type DescriptionUnion[Structure, Molecule]
A pymatgen Structure
or Molecule
based on the periodicity of the
Union[Structure, Molecule]
OPTIMADE structure.
Source code inoptimade/adapters/structures/pymatgen.py
def get_pymatgen(optimade_structure: OptimadeStructure) -> Union[Structure, Molecule]:\n \"\"\"Get pymatgen `Structure` or `Molecule` from OPTIMADE structure.\n\n This function will return either a pymatgen `Structure` or `Molecule` based\n on the periodicity or periodic dimensionality of OPTIMADE structure.\n\n For structures that are periodic in one or more dimensions, a pymatgen `Structure` is returned when valid lattice_vectors are given.\n This means, if the any of the values in the [`dimension_types`][optimade.models.structures.StructureResourceAttributes.dimension_types]\n attribute is `1`s or if [`nperiodic_dimesions`][optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions] > 0.\n\n Otherwise, a pymatgen `Molecule` is returned.\n\n Parameters:\n optimade_structure: OPTIMADE structure.\n\n Returns:\n A pymatgen `Structure` or `Molecule` based on the periodicity of the\n OPTIMADE structure.\n\n \"\"\"\n if \"optimade.adapters\" in repr(globals().get(\"Structure\")):\n warn(PYMATGEN_NOT_FOUND, AdapterPackageNotFound)\n return None\n\n if valid_lattice_vector(optimade_structure.attributes.lattice_vectors) and ( # type: ignore[arg-type]\n optimade_structure.attributes.nperiodic_dimensions > 0 # type: ignore[operator]\n or any(optimade_structure.attributes.dimension_types) # type: ignore[arg-type]\n ):\n return _get_structure(optimade_structure)\n\n return _get_molecule(optimade_structure)\n
"},{"location":"api_reference/adapters/structures/utils/","title":"utils","text":"Utility functions to help the conversion functions along.
Most of these functions rely on the NumPy library.
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.cell_to_cellpar","title":"cell_to_cellpar(cell, radians=False)
","text":"Returns the cell parameters [a, b, c, alpha, beta, gamma]
.
Angles are in degrees unless radian=True
is used.
Based on ASE code.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
radians
bool
Use radians instead of degrees (default) for angles.
False
Returns:
Type Descriptionlist[float]
The unit cell parameters as a list
of float
values.
optimade/adapters/structures/utils.py
def cell_to_cellpar(\n cell: tuple[Vector3D, Vector3D, Vector3D], radians: bool = False\n) -> list[float]:\n \"\"\"Returns the cell parameters `[a, b, c, alpha, beta, gamma]`.\n\n Angles are in degrees unless `radian=True` is used.\n\n Note:\n Based on [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/geometry/cell.html#cell_to_cellpar).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n radians: Use radians instead of degrees (default) for angles.\n\n Returns:\n The unit cell parameters as a `list` of `float` values.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell = np.asarray(cell)\n\n lengths = [np.linalg.norm(vector) for vector in cell]\n angles = []\n for i in range(3):\n j = i - 1\n k = i - 2\n outer_product = lengths[j] * lengths[k]\n if outer_product > 1e-16:\n x_vector = np.dot(cell[j], cell[k]) / outer_product\n angle = 180.0 / np.pi * np.arccos(x_vector)\n else:\n angle = 90.0\n angles.append(angle)\n if radians:\n angles = [angle * np.pi / 180 for angle in angles]\n return np.array(lengths + angles)\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.cellpar_to_cell","title":"cellpar_to_cell(cellpar, ab_normal=(0, 0, 1), a_direction=None)
","text":"Return a 3x3 cell matrix from cellpar=[a,b,c,alpha,beta,gamma]
.
Angles must be in degrees.
The returned cell is orientated such that a and b are normal to ab_normal
and a is parallel to the projection of a_direction
in the a-b plane.
Default a_direction
is (1,0,0), unless this is parallel to ab_normal
, in which case default a_direction
is (0,0,1).
The returned cell has the vectors va, vb and vc along the rows. The cell will be oriented such that va and vb are normal to ab_normal
and va will be along the projection of a_direction
onto the a-b plane.
cell = cellpar_to_cell([1, 2, 4, 10, 20, 30], (0, 1, 1), (1, 2, 3)) np.round(cell, 3) array([[ 0.816, -0.408, 0.408], [ 1.992, -0.13 , 0.13 ], [ 3.859, -0.745, 0.745]])
NoteDirect copy of ASE code.
Parameters:
Name Type Description Defaultcellpar
list[float]
The unit cell parameters as a list
of float
values.
Note: The angles must be given in degrees.
requiredab_normal
tuple[int, int, int]
Unit vector normal to the ab-plane.
(0, 0, 1)
a_direction
Optional[tuple[int, int, int]]
Unit vector defining the a-direction (default: (1, 0, 0)
).
None
Returns:
Type Descriptionlist[Vector3D]
A Cartesian 3x3 cell.
list[Vector3D]
This should equate to the
list[Vector3D]
lattice_vectors
attribute.
optimade/adapters/structures/utils.py
def cellpar_to_cell(\n cellpar: list[float],\n ab_normal: tuple[int, int, int] = (0, 0, 1),\n a_direction: Optional[tuple[int, int, int]] = None,\n) -> list[Vector3D]:\n \"\"\"Return a 3x3 cell matrix from `cellpar=[a,b,c,alpha,beta,gamma]`.\n\n Angles must be in degrees.\n\n The returned cell is orientated such that a and b\n are normal to `ab_normal` and a is parallel to the projection of\n `a_direction` in the a-b plane.\n\n Default `a_direction` is (1,0,0), unless this is parallel to\n `ab_normal`, in which case default `a_direction` is (0,0,1).\n\n The returned cell has the vectors va, vb and vc along the rows. The\n cell will be oriented such that va and vb are normal to `ab_normal`\n and va will be along the projection of `a_direction` onto the a-b\n plane.\n\n Example:\n >>> cell = cellpar_to_cell([1, 2, 4, 10, 20, 30], (0, 1, 1), (1, 2, 3))\n >>> np.round(cell, 3)\n array([[ 0.816, -0.408, 0.408],\n [ 1.992, -0.13 , 0.13 ],\n [ 3.859, -0.745, 0.745]])\n\n Note:\n Direct copy of [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/geometry/cell.html#cellpar_to_cell).\n\n Parameters:\n cellpar: The unit cell parameters as a `list` of `float` values.\n\n **Note**: The angles must be given in degrees.\n ab_normal: Unit vector normal to the ab-plane.\n a_direction: Unit vector defining the a-direction (default: `(1, 0, 0)`).\n\n Returns:\n A Cartesian 3x3 cell.\n\n This should equate to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n if a_direction is None:\n if np.linalg.norm(np.cross(ab_normal, (1, 0, 0))) < 1e-5:\n a_direction = (0, 0, 1)\n else:\n a_direction = (1, 0, 0)\n\n # Define rotated X,Y,Z-system, with Z along ab_normal and X along\n # the projection of a_direction onto the normal plane of Z.\n a_direction_array = np.array(a_direction)\n Z = unit_vector(ab_normal) # type: ignore\n X = unit_vector(a_direction_array - np.dot(a_direction_array, Z) * Z)\n Y = np.cross(Z, X)\n\n # Express va, vb and vc in the X,Y,Z-system\n alpha, beta, gamma = 90.0, 90.0, 90.0\n if isinstance(cellpar, (int, float)):\n a = b = c = cellpar\n elif len(cellpar) == 1:\n a = b = c = cellpar[0]\n elif len(cellpar) == 3:\n a, b, c = cellpar\n else:\n a, b, c, alpha, beta, gamma = cellpar\n\n # Handle orthorhombic cells separately to avoid rounding errors\n eps = 2 * np.spacing(90.0, dtype=np.float64) # around 1.4e-14\n # alpha\n if abs(abs(alpha) - 90) < eps:\n cos_alpha = 0.0\n else:\n cos_alpha = np.cos(alpha * np.pi / 180.0)\n # beta\n if abs(abs(beta) - 90) < eps:\n cos_beta = 0.0\n else:\n cos_beta = np.cos(beta * np.pi / 180.0)\n # gamma\n if abs(gamma - 90) < eps:\n cos_gamma = 0.0\n sin_gamma = 1.0\n elif abs(gamma + 90) < eps:\n cos_gamma = 0.0\n sin_gamma = -1.0\n else:\n cos_gamma = np.cos(gamma * np.pi / 180.0)\n sin_gamma = np.sin(gamma * np.pi / 180.0)\n\n # Build the cell vectors\n va = a * np.array([1, 0, 0])\n vb = b * np.array([cos_gamma, sin_gamma, 0])\n cx = cos_beta\n cy = (cos_alpha - cos_beta * cos_gamma) / sin_gamma\n cz_sqr = 1.0 - cx * cx - cy * cy\n assert cz_sqr >= 0\n cz = np.sqrt(cz_sqr)\n vc = c * np.array([cx, cy, cz])\n\n # Convert to the Cartesian x,y,z-system\n abc = np.vstack((va, vb, vc))\n T = np.vstack((X, Y, Z))\n cell = np.dot(abc, T)\n\n return cell\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.elements_ratios_from_species_at_sites","title":"elements_ratios_from_species_at_sites(species_at_sites)
","text":"Compute the OPTIMADE elements_ratios
field from species_at_sites
in the case where species_at_sites
refers to sites wholly occupied by the given elements, e.g., not arbitrary species labels or with partial/mixed occupancy.
optimade/adapters/structures/utils.py
def elements_ratios_from_species_at_sites(species_at_sites: list[str]) -> list[float]:\n \"\"\"Compute the OPTIMADE `elements_ratios` field from `species_at_sites` in the case where `species_at_sites` refers\n to sites wholly occupied by the given elements, e.g., not arbitrary species labels or with partial/mixed occupancy.\n\n \"\"\"\n elements = set(species_at_sites)\n counts = {e: species_at_sites.count(e) for e in elements}\n num_sites = len(species_at_sites)\n return [counts[e] / num_sites for e in sorted(elements)]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.fractional_coordinates","title":"fractional_coordinates(cell, cartesian_positions)
","text":"Returns fractional coordinates and wraps coordinates to [0,1[
.
Based on ASE code.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
cartesian_positions
list[Vector3D]
A list of cartesian atomic positions. This equates to the cartesian_site_positions
attribute.
Returns:
Type Descriptionlist[Vector3D]
A list of fractional coordinates for the atomic positions.
Source code inoptimade/adapters/structures/utils.py
def fractional_coordinates(\n cell: tuple[Vector3D, Vector3D, Vector3D], cartesian_positions: list[Vector3D]\n) -> list[Vector3D]:\n \"\"\"Returns fractional coordinates and wraps coordinates to `[0,1[`.\n\n Note:\n Based on [ASE code](https://wiki.fysik.dtu.dk/ase/_modules/ase/atoms.html#Atoms.get_scaled_positions).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n cartesian_positions: A list of cartesian atomic positions. This equates to the\n [`cartesian_site_positions`][optimade.models.structures.StructureResourceAttributes.cartesian_site_positions]\n attribute.\n\n Returns:\n A list of fractional coordinates for the atomic positions.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell_array = np.asarray(cell)\n cartesian_positions_array = np.asarray(cartesian_positions)\n\n fractional = np.linalg.solve(cell_array.T, cartesian_positions_array.T).T\n\n # Expecting a bulk 3D structure here, note, this may change in the future.\n # See `ase.atoms:Atoms.get_scaled_positions()` for ideas on how to handle lower dimensional structures.\n # Furthermore, according to ASE we need to modulo 1.0 twice.\n # This seems to be due to small floats % 1.0 becomes 1.0, hence twice makes it 0.0.\n for i in range(3):\n fractional[:, i] %= 1.0\n fractional[:, i] %= 1.0\n\n return [tuple(position) for position in fractional] # type: ignore\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.pad_cell","title":"pad_cell(lattice_vectors, padding=None)
","text":"Turn any null
/None
values into a float
in given tuple
of lattice_vectors
.
Parameters:
Name Type Description Defaultlattice_vectors
tuple[Vector3D, Vector3D, Vector3D]
A 3x3 cartesian cell. This is the lattice_vectors
attribute.
padding
Optional[float]
A value with which null
or None
values should be replaced.
None
Returns:
Type Descriptiontuple
The possibly redacted/padded lattice_vectors
and a bool
declaring whether or not
tuple
the value has been redacted/padded or not, i.e., whether it contained null
or None
tuple
values.
Source code inoptimade/adapters/structures/utils.py
def pad_cell(\n lattice_vectors: tuple[Vector3D, Vector3D, Vector3D],\n padding: Optional[float] = None,\n) -> tuple: # Setting this properly makes MkDocs fail.\n \"\"\"Turn any `null`/`None` values into a `float` in given `tuple` of\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors].\n\n Parameters:\n lattice_vectors: A 3x3 cartesian cell. This is the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors]\n attribute.\n padding: A value with which `null` or `None` values should be replaced.\n\n Returns:\n The possibly redacted/padded `lattice_vectors` and a `bool` declaring whether or not\n the value has been redacted/padded or not, i.e., whether it contained `null` or `None`\n values.\n\n \"\"\"\n return _pad_iter_of_iters(\n iterable=lattice_vectors,\n padding=padding,\n outer=tuple,\n inner=tuple,\n )\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.scaled_cell","title":"scaled_cell(cell)
","text":"Return a scaled 3x3 cell from cartesian 3x3 cell (lattice_vectors
). This 3x3 matrix can be used to calculate the fractional coordinates from the cartesian_site_positions.
This is based on PDB's method of calculating SCALE from CRYST data. For more info, see this site.
Parameters:
Name Type Description Defaultcell
tuple[Vector3D, Vector3D, Vector3D]
A Cartesian 3x3 cell. This equates to the lattice_vectors
attribute.
Returns:
Type Descriptiontuple[Vector3D, Vector3D, Vector3D]
A scaled 3x3 cell.
Source code inoptimade/adapters/structures/utils.py
def scaled_cell(\n cell: tuple[Vector3D, Vector3D, Vector3D],\n) -> tuple[Vector3D, Vector3D, Vector3D]:\n \"\"\"Return a scaled 3x3 cell from cartesian 3x3 cell (`lattice_vectors`).\n This 3x3 matrix can be used to calculate the fractional coordinates from the cartesian_site_positions.\n\n This is based on PDB's method of calculating SCALE from CRYST data.\n For more info, see [this site](https://www.wwpdb.org/documentation/file-format-content/format33/sect8.html#SCALEn).\n\n Parameters:\n cell: A Cartesian 3x3 cell. This equates to the\n [`lattice_vectors`][optimade.models.structures.StructureResourceAttributes.lattice_vectors] attribute.\n\n Returns:\n A scaled 3x3 cell.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n cell = np.asarray(cell)\n\n volume = np.dot(cell[0], np.cross(cell[1], cell[2]))\n scale = []\n for i in range(3):\n vector = np.cross(cell[(i + 1) % 3], cell[(i + 2) % 3]) / volume\n scale.append(tuple(vector))\n return tuple(scale) # type: ignore[return-value]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.species_from_species_at_sites","title":"species_from_species_at_sites(species_at_sites)
","text":"When a list of species dictionaries is not provided, this function can be used to infer the species from the provided species_at_sites.
In this use case, species_at_sites is assumed to provide a list of element symbols, and refers to situations with no mixed occupancy, i.e., the constructed species list will contain all unique species with concentration equal to 1 and the species_at_site tag will be used as the chemical symbol.
Parameters:
Name Type Description Defaultspecies_at_sites
list[str]
The list found under the species_at_sites field.
requiredReturns:
Type Descriptionlist[Species]
An OPTIMADE species list.
Source code inoptimade/adapters/structures/utils.py
def species_from_species_at_sites(\n species_at_sites: list[str],\n) -> list[OptimadeStructureSpecies]:\n \"\"\"When a list of species dictionaries is not provided, this function\n can be used to infer the species from the provided species_at_sites.\n\n In this use case, species_at_sites is assumed to provide a list of\n element symbols, and refers to situations with no mixed occupancy, i.e.,\n the constructed species list will contain all unique species with\n concentration equal to 1 and the species_at_site tag will be used as\n the chemical symbol.\n\n Parameters:\n species_at_sites: The list found under the species_at_sites field.\n\n Returns:\n An OPTIMADE species list.\n\n \"\"\"\n return [\n OptimadeStructureSpecies(name=_, concentration=[1.0], chemical_symbols=[_])\n for _ in set(species_at_sites)\n ]\n
"},{"location":"api_reference/adapters/structures/utils/#optimade.adapters.structures.utils.unit_vector","title":"unit_vector(x)
","text":"Return a unit vector in the same direction as x
.
Parameters:
Name Type Description Defaultx
Vector3D
A three-dimensional vector.
requiredReturns:
Type DescriptionVector3D
A unit vector in the same direction as x
.
optimade/adapters/structures/utils.py
def unit_vector(x: Vector3D) -> Vector3D:\n \"\"\"Return a unit vector in the same direction as `x`.\n\n Parameters:\n x: A three-dimensional vector.\n\n Returns:\n A unit vector in the same direction as `x`.\n\n \"\"\"\n if globals().get(\"np\", None) is None:\n warn(NUMPY_NOT_FOUND, AdapterPackageNotFound)\n return None # type: ignore[return-value]\n\n y = np.array(x, dtype=\"float\")\n return y / np.linalg.norm(y) # type: ignore\n
"},{"location":"api_reference/client/cli/","title":"cli","text":""},{"location":"api_reference/client/client/","title":"client","text":"This module implements OPTIMADE client functionality for:
OptimadeClient
","text":"This class implemements a client for executing the same queries across multiple OPTIMADE APIs simultaneously, paging and caching the results.
By default, all registered OPTIMADE providers will be queried simulateneously and asynchronously, with the results collected into the all_results
attribute, keyed by endpoint, filter and provider.
optimade/client/client.py
class OptimadeClient:\n \"\"\"This class implemements a client for executing the same queries\n across multiple OPTIMADE APIs simultaneously, paging and caching the\n results.\n\n By default, all registered OPTIMADE providers will be queried\n simulateneously and asynchronously, with the results collected\n into the `all_results` attribute, keyed by endpoint, filter\n and provider.\n\n \"\"\"\n\n base_urls: Union[str, Iterable[str]]\n \"\"\"A list (or any iterable) of OPTIMADE base URLs to query.\"\"\"\n\n all_results: dict[str, dict[str, dict[str, QueryResults]]] = defaultdict(dict)\n \"\"\"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains\n the results from each base URL for that particular filter.\n \"\"\"\n\n count_results: dict[str, dict[str, dict[str, int]]] = defaultdict(dict)\n \"\"\"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains\n the number of results from each base URL for that particular filter.\n \"\"\"\n\n max_results_per_provider: Optional[int] = None\n \"\"\"Maximum number of results to downlod per provider. If None, will\n download all.\n \"\"\"\n\n property_lists: dict[str, dict[str, list[str]]] = defaultdict(dict)\n \"\"\"A dictionary containing list of properties served by each database,\n broken down by entry type, then database.\n \"\"\"\n\n headers: dict = {\"User-Agent\": f\"optimade-python-tools/{__version__}\"}\n \"\"\"Additional HTTP headers.\"\"\"\n\n http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0)\n \"\"\"The timeout to use for each HTTP request.\"\"\"\n\n max_attempts: int\n \"\"\"The maximum number of times to repeat a failed query before giving up.\"\"\"\n\n use_async: bool\n \"\"\"Whether or not to make all requests asynchronously using asyncio.\"\"\"\n\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None\n \"\"\"A list of callbacks to execute after each successful request, used\n to e.g., write to a file, add results to a database or perform additional\n filtering.\n\n The callbacks will receive the request URL and the results extracted\n from the JSON response, with keys 'data', 'meta', 'links', 'errors'\n and 'included'.\n\n Each callback can return a dictionary that can modify the `next_url` with the\n key `next` and the progress bar with the key `advance_results`.\n In the case of multiple provided callbacks, only the value returned by the final\n callback in the stack will be used.\n\n \"\"\"\n\n count_binary_search: bool = True\n \"\"\"Enable binary search count for databases that do not support `meta->data_returned`.\"\"\"\n\n silent: bool\n \"\"\"Whether to disable progress bar printing.\"\"\"\n\n skip_ssl: bool = False\n \"\"\"Whether to skip SSL verification.\"\"\"\n\n _excluded_providers: Optional[set[str]] = None\n \"\"\"A set of providers IDs excluded from future queries.\"\"\"\n\n _included_providers: Optional[set[str]] = None\n \"\"\"A set of providers IDs included from future queries.\"\"\"\n\n _excluded_databases: Optional[set[str]] = None\n \"\"\"A set of child database URLs excluded from future queries.\"\"\"\n\n __current_endpoint: Optional[str] = None\n \"\"\"Used internally when querying via `client.structures.get()` to set the\n chosen endpoint. Should be reset to `None` outside of all `get()` calls.\"\"\"\n\n _http_client: Optional[Union[type[httpx.AsyncClient], type[requests.Session]]] = (\n None\n )\n \"\"\"Override the HTTP client class, primarily used for testing.\"\"\"\n\n __strict_async: bool = False\n \"\"\"Whether or not to fallover if `use_async` is true yet asynchronous mode\n is impossible due to, e.g., a running event loop.\n \"\"\"\n\n _force_binary_search: bool = False\n \"\"\"Setting to test binary searches in cases where servers do return\n the count.\n \"\"\"\n\n def __init__(\n self,\n base_urls: Optional[Union[str, Iterable[str]]] = None,\n max_results_per_provider: int = 1000,\n headers: Optional[dict] = None,\n http_timeout: Optional[Union[httpx.Timeout, float]] = None,\n max_attempts: int = 5,\n use_async: bool = True,\n silent: bool = False,\n exclude_providers: Optional[list[str]] = None,\n include_providers: Optional[list[str]] = None,\n exclude_databases: Optional[list[str]] = None,\n http_client: Optional[\n Union[type[httpx.AsyncClient], type[requests.Session]]\n ] = None,\n verbosity: int = 0,\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None,\n skip_ssl: bool = False,\n ):\n \"\"\"Create the OPTIMADE client object.\n\n Parameters:\n base_urls: A list of OPTIMADE base URLs to query.\n max_results_per_provider: The maximum number of results to download\n from each provider (-1 or 0 indicate unlimited).\n headers: Any additional HTTP headers to use for the queries.\n http_timeout: The timeout to use per request. Defaults to 10\n seconds with 1000 seconds for reads specifically. Overriding this value\n will replace all timeouts (connect, read, write and pool) with this value.\n max_attempts: The maximum number of times to repeat a failing query.\n use_async: Whether or not to make all requests asynchronously.\n exclude_providers: A set or collection of provider IDs to exclude from queries.\n include_providers: A set or collection of provider IDs to include in queries.\n exclude_databases: A set or collection of child database URLs to exclude from queries.\n http_client: An override for the underlying HTTP client, primarily used for testing.\n callbacks: A list of functions to call after each successful response, see the\n attribute [`OptimadeClient.callbacks`][optimade.client.OptimadeClient.callbacks]\n docstring for more details.\n verbosity: The verbosity level of the client.\n\n \"\"\"\n\n self.max_results_per_provider = max_results_per_provider\n if self.max_results_per_provider in (-1, 0):\n self.max_results_per_provider = None\n\n self._excluded_providers = set(exclude_providers) if exclude_providers else None\n self._included_providers = set(include_providers) if include_providers else None\n self._excluded_databases = set(exclude_databases) if exclude_databases else None\n\n self.max_attempts = max_attempts\n self.silent = silent\n self.verbosity = verbosity\n self.skip_ssl = skip_ssl\n\n if headers:\n self.headers.update(headers)\n\n if not base_urls:\n progress = None\n if not self.silent:\n progress = OptimadeClientProgress()\n self.base_urls = list(\n get_all_databases(\n exclude_providers=self._excluded_providers,\n include_providers=self._included_providers,\n exclude_databases=self._excluded_databases,\n progress=progress,\n skip_ssl=self.skip_ssl,\n )\n )\n else:\n if exclude_providers or include_providers or exclude_databases:\n raise RuntimeError(\n \"Cannot provide both a list of base URLs and included/excluded databases.\"\n )\n\n self.base_urls = base_urls\n\n if isinstance(self.base_urls, str):\n self.base_urls = [self.base_urls]\n self.base_urls = list(self.base_urls)\n\n if not self.base_urls:\n raise SystemExit(\n \"Unable to access any OPTIMADE base URLs. If you believe this is an error, try manually specifying some base URLs.\"\n )\n\n if http_timeout:\n if isinstance(http_timeout, httpx.Timeout):\n self.http_timeout = http_timeout\n else:\n self.http_timeout = httpx.Timeout(http_timeout)\n\n self.use_async = use_async\n\n if http_client:\n self._http_client = http_client\n if issubclass(self._http_client, httpx.AsyncClient):\n if not self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use synchronous mode with an asynchronous HTTP client, please set `use_async=True` or pass an asynchronous HTTP client.\"\n )\n self.use_async = True\n elif issubclass(self._http_client, requests.Session):\n if self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use async mode with a synchronous HTTP client, please set `use_async=False` or pass an synchronous HTTP client.\"\n )\n self.use_async = False\n else:\n if use_async:\n self._http_client = httpx.AsyncClient\n else:\n self._http_client = requests.Session\n\n self.callbacks = callbacks\n\n def __getattribute__(self, name):\n \"\"\"Allows entry endpoints to be queried via attribute access, using the\n allowed list for this module.\n\n Should also pass through any `extensions/<example>` endpoints.\n\n Any non-entry-endpoint name requested will be passed to the\n original `__getattribute__`.\n\n !!! example\n ```python\n from optimade.client import OptimadeClient\n cli = OptimadeClient()\n structures = cli.structures.get()\n references = cli.references.get()\n info_structures = cli.info.structures.get()\n ```\n\n \"\"\"\n if name in ENDPOINTS:\n if self.__current_endpoint == \"info\":\n self.__current_endpoint = f\"info/{name}\"\n elif self.__current_endpoint == \"extensions\":\n self.__current_endpoint = f\"extensions/{name}\"\n else:\n self.__current_endpoint = name\n return self\n\n return super().__getattribute__(name)\n\n def get(\n self,\n filter: Optional[str] = None,\n endpoint: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n ) -> dict[str, dict[str, dict[str, dict]]]:\n \"\"\"Gets the results from the endpoint and filter across the\n defined OPTIMADE APIs.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Performing query [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n response_fields=response_fields,\n page_limit=None,\n paginate=True,\n sort=sort,\n )\n self.all_results[endpoint][filter] = results\n return {endpoint: {filter: {k: results[k].asdict() for k in results}}}\n\n def count(\n self, filter: Optional[str] = None, endpoint: Optional[str] = None\n ) -> dict[str, dict[str, dict[str, Optional[int]]]]:\n \"\"\"Counts the number of results for the filter, requiring\n only 1 request per provider by making use of the `meta->data_returned`\n key. If missing, attempts will be made to perform an exponential/binary\n search over pagination to count the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the number of query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Counting results for [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n page_limit=1,\n paginate=False,\n response_fields=[],\n sort=None,\n )\n count_results = {}\n\n for base_url in results:\n count_results[base_url] = results[base_url].meta.get(\n \"data_returned\", None\n )\n\n if count_results[base_url] is None or self._force_binary_search:\n if self.count_binary_search:\n count_results[base_url] = self.binary_search_count(\n filter, endpoint, base_url, results\n )\n else:\n self._progress.print(\n f\"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results. Full response: {results[base_url]}\"\n )\n\n self.count_results[endpoint][filter] = count_results\n return {endpoint: {filter: count_results}}\n\n def binary_search_count(\n self, filter: str, endpoint: str, base_url: str, results: Optional[dict] = None\n ) -> int:\n \"\"\"In cases where `data_returned` is not available (due to database limitations or\n otherwise), iteratively probe the final page of results available for a filter using\n binary search.\n\n Note: These queries always happen synchronously across APIs, but can be executed\n asynchronously within a single API.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n results: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if self.verbosity:\n self._progress.print(f\"Performing binary search count for {base_url}\")\n if self.use_async:\n return self._binary_search_count_async(filter, endpoint, base_url, results)\n\n else:\n raise NotImplementedError(\n \"Binary search count is not yet implemented for synchronous queries.\"\n )\n\n def _binary_search_count_async(\n self, filter: str, endpoint: str, base_url: str, result: Optional[dict] = None\n ) -> int:\n \"\"\"Run a series of asynchronously queries on a given API to\n find the number of results for a filter.\n\n Starting with logarithmically spaced page offsets, iteratively probe\n the final page of results available for a filter.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n result: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if result is None:\n # first a check that there are any results at all\n result = asyncio.run(\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=1,\n response_fields=[],\n paginate=False,\n )\n )\n if self.verbosity:\n self._progress.print(\"Definitely found results\")\n\n if not result[base_url].data:\n return 0\n\n attempts = 0\n max_attempts = 100\n\n window, probe = self._update_probe_and_window()\n\n while attempts < max_attempts:\n self._progress.disable = True\n\n result = asyncio.run(\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=1,\n response_fields=[],\n paginate=False,\n other_params={\"page_offset\": probe},\n )\n )\n\n self._progress.disable = self.silent\n\n window, probe = self._update_probe_and_window(\n window, probe, bool(result[base_url].data)\n )\n\n if window[0] == window[1] and window[0] == probe:\n return probe\n\n attempts += 1\n\n if self.verbosity > 2:\n self._progress.print(f\"Binary search debug info: {window=}, {probe=}\")\n\n else:\n message = f\"Exceeded maximum number of attempts for binary search on {base_url}, {filter=}\"\n self._progress.print(message)\n raise RuntimeError(message)\n\n @staticmethod\n def _update_probe_and_window(\n window: Optional[tuple[int, Optional[int]]] = None,\n last_probe: Optional[int] = None,\n below: Optional[bool] = None,\n ) -> tuple[tuple[int, Optional[int]], int]:\n \"\"\"Sets the new range, trial value and exit condition for exponential/binary search.\n When converged, returns the same value three times.\n\n Parameters:\n window: The current window of results.\n last_probe: The last probe value.\n below: Whether the last probe was below the target value.\n\n Returns:\n A tuple of the new window and probe value,\n or the count three times if converged.\n\n \"\"\"\n\n if window is None and last_probe is None:\n return (1, None), 1_000_000\n\n if window is None or last_probe is None:\n raise RuntimeError(\n \"Invalid arguments: must provide all or none of window, last_probe and below parameters\"\n )\n\n probe: int = last_probe\n\n # Exit condition: find a range of (count, count+1) values\n # and determine whether the probe was above or below in the last guess\n if window[1] is not None and window[1] - window[0] == 1:\n if below:\n return (window[0], window[0]), window[0]\n else:\n return (window[1], window[1]), window[1]\n\n # Enclose the real value in the window, with `None` indicating an open boundary\n if below:\n window = (last_probe, window[1])\n else:\n window = (window[0], last_probe)\n\n # If we've not reached the upper bound yet, try 10x\n if window[1] is None:\n probe *= 10\n\n # Otherwise, if we're in the window and the ends of the window now have the same power of 10, take the average (102 => 108) => 105\n elif round(math.log10(window[0])) == round(math.log10(window[0])):\n probe = (window[1] + window[0]) // 2\n # otherwise use logarithmic average (10, 1000) => 100\n else:\n probe = int(10 ** (math.log10(window[1]) + math.log10(window[0]) / 2))\n\n return window, probe\n\n def list_properties(\n self,\n entry_type: str,\n ) -> dict[str, list[str]]:\n \"\"\"Returns the list of properties reported at `/info/<entry_type>`\n for the given entry type, for each database.\n\n \"\"\"\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Listing properties for [bold yellow]{entry_type}[/bold yellow]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n \"\",\n f\"info/{entry_type}\",\n paginate=False,\n page_limit=1,\n response_fields=[],\n sort=None,\n )\n self.property_lists = {entry_type: {}}\n for database in results:\n self.property_lists[entry_type][database] = list(\n results[database].data.get(\"properties\", {}).keys() # type: ignore\n )\n return self.property_lists[entry_type]\n\n def search_property(self, query: str, entry_type: str) -> dict[str, list[str]]:\n \"\"\"Searches for the query substring within the listed properties\n served by each database.\n\n Parameters:\n query: The substring to search for.\n entry_type: The entry type to query.\n\n Returns:\n A nested dictionary of matching property lists, arranged by\n entry type and database.\n\n \"\"\"\n if not self.property_lists:\n self.list_properties(entry_type=entry_type)\n\n matching_properties: dict[str, dict[str, list[str]]] = {\n entry_type: defaultdict(list)\n }\n if entry_type in self.property_lists:\n for database in self.property_lists[entry_type]:\n for property in self.property_lists[entry_type][database]:\n if query in property:\n matching_properties[entry_type][database].append(property)\n return matching_properties[entry_type]\n\n def _execute_queries(\n self,\n filter: str,\n endpoint: str,\n page_limit: Optional[int],\n paginate: bool,\n response_fields: Optional[list[str]],\n sort: Optional[str],\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the queries over the base URLs either asynchronously or\n serially, depending on the `self.use_async` setting.\n\n Parameters:\n filter: The OPTIMADE filter string.\n endpoint: The OPTIMADE endpoint to query.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Returns:\n A mapping from base URL to `QueryResults` for each queried API.\n\n \"\"\"\n if self.use_async:\n # Check for a pre-existing event loop (e.g. within a Jupyter notebook)\n # and use it if present\n try:\n event_loop = asyncio.get_running_loop()\n if event_loop:\n if self.__strict_async:\n raise RuntimeError(\n \"Detected a running event loop, cannot run in async mode.\"\n )\n self._progress.print(\n \"Detected a running event loop (e.g., Jupyter). Attempting to switch to synchronous mode.\"\n )\n self.use_async = False\n self._http_client = requests.Session\n except RuntimeError:\n event_loop = None\n\n if self.use_async and not event_loop:\n return asyncio.run(\n self._get_all_async(\n endpoint,\n filter,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n )\n )\n\n return self._get_all(\n endpoint,\n filter,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n )\n\n def get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously on one API.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return self._get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]\"\n )\n return {base_url: error_query_results}\n\n async def _get_all_async(\n self,\n endpoint: str,\n filter: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n base_urls: Optional[Iterable[str]] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously across all defined APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n base_urls: A list of base URLs to query (defaults to `self.base_urls`).\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n if not base_urls:\n base_urls = self.base_urls\n\n results = await asyncio.gather(\n *[\n self.get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n for base_url in base_urls\n ]\n )\n return functools.reduce(lambda r1, r2: {**r1, **r2}, results)\n\n def _get_all(\n self,\n endpoint: str,\n filter: str,\n page_limit: Optional[int] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n paginate: bool = True,\n base_urls: Optional[Iterable[str]] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously across all defined APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n base_urls: A list of base URLs to query (defaults to `self.base_urls`).\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n if not base_urls:\n base_urls = self.base_urls\n results = [\n self.get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n for base_url in base_urls\n ]\n if results:\n return functools.reduce(lambda r1, r2: {**r1, **r2}, results)\n\n return {}\n\n async def get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously on one API.\n\n !!! note\n This method currently makes non-blocking requests\n to a single API, but these requests are executed\n serially on that API, i.e., results are pulled one\n page at a time, but requests will not block other\n async requests to other APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return await self._get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{error_query_results.errors}[/red i]\"\n )\n return {base_url: error_query_results}\n\n async def _get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"See [`OptimadeClient.get_one_async`][optimade.client.OptimadeClient.get_one_async].\"\"\"\n next_url, _task = self._setup(\n endpoint=endpoint,\n base_url=base_url,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n results = QueryResults()\n try:\n async with self._http_client(headers=self.headers) as client: # type: ignore[union-attr,call-arg,misc]\n while next_url:\n attempts = 0\n try:\n if self.verbosity:\n self._progress.print(\n f\"Making request to {next_url!r} {attempts=}\"\n )\n r = await client.get(\n next_url, follow_redirects=True, timeout=self.http_timeout\n )\n page_results, next_url = self._handle_response(r, _task)\n except RecoverableHTTPError:\n attempts += 1\n if attempts > self.max_attempts:\n raise RuntimeError(\n f\"Exceeded maximum number of retries for {next_url}\"\n )\n await asyncio.sleep(1)\n continue\n\n results.update(page_results)\n\n if not paginate:\n break\n\n if (\n self.max_results_per_provider\n and len(results.data) >= self.max_results_per_provider\n ):\n if not self.silent:\n self._progress.print(\n f\"Reached {len(results.data)} results for {base_url}, exceeding `max_results_per_provider` parameter ({self.max_results_per_provider}). Stopping download.\"\n )\n break\n\n return {str(base_url): results}\n\n finally:\n self._teardown(_task, len(results.data))\n\n def _get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n response_fields: Optional[list[str]] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n ) -> dict[str, QueryResults]:\n \"\"\"See [`OptimadeClient.get_one`][optimade.client.OptimadeClient.get_one].\"\"\"\n next_url, _task = self._setup(\n endpoint=endpoint,\n base_url=base_url,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n results = QueryResults()\n try:\n with self._http_client() as client: # type: ignore[misc]\n client.headers.update(self.headers)\n\n if isinstance(client, requests.Session):\n # Convert configured httpx timeout to requests-style tuple\n timeout = (self.http_timeout.connect, self.http_timeout.read)\n\n while next_url:\n attempts = 0\n try:\n if self.verbosity:\n self._progress.print(\n f\"Making request to {next_url!r} {attempts=}\"\n )\n r = client.get(next_url, timeout=timeout)\n page_results, next_url = self._handle_response(r, _task)\n except RecoverableHTTPError:\n attempts += 1\n if attempts > self.max_attempts:\n raise RuntimeError(\n f\"Exceeded maximum number of retries for {next_url}\"\n )\n time.sleep(1)\n continue\n\n results.update(page_results)\n\n if (\n self.max_results_per_provider\n and len(results.data) >= self.max_results_per_provider\n ):\n if not self.silent:\n self._progress.print(\n f\"Reached {len(results.data)} results for {base_url}, exceeding `max_results_per_provider` parameter ({self.max_results_per_provider}). Stopping download.\"\n )\n break\n\n if not paginate:\n break\n\n return {str(base_url): results}\n\n finally:\n self._teardown(_task, len(results.data))\n\n def _setup(\n self,\n endpoint: str,\n base_url: str,\n filter: str,\n page_limit: Optional[int],\n response_fields: Optional[list[str]],\n sort: Optional[str],\n other_params: Optional[dict[str, Any]] = None,\n ) -> tuple[str, TaskID]:\n \"\"\"Constructs the first query URL and creates the progress bar task.\n\n Returns:\n The URL for the first query and the Rich TaskID for progress logging.\n\n \"\"\"\n url = self._build_url(\n base_url=base_url,\n endpoint=endpoint,\n filter=filter,\n page_limit=page_limit,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n parsed_url = urlparse(url)\n _task = self._progress.add_task(\n description=parsed_url.netloc + parsed_url.path,\n total=None,\n )\n return url, _task\n\n def _build_url(\n self,\n base_url: str,\n endpoint: Optional[str] = \"structures\",\n version: Optional[str] = None,\n filter: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n other_params: Optional[dict[str, Any]] = None,\n ) -> str:\n \"\"\"Builds the URL to query based on the passed parameters.\n\n Parameters:\n base_url: The server's base URL.\n endpoint: The endpoint to query.\n version: The OPTIMADE version string.\n filter: The filter to apply to the endpoint.\n response_fields: A list of response fields to request from the server.\n sort: The field by which to sort the results.\n page_limit: The page limit for an individual request.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n The overall query URL, including parameters.\n\n \"\"\"\n\n if not version:\n version = f'v{__api_version__.split(\".\")[0]}'\n while base_url.endswith(\"/\"):\n base_url = base_url[:-1]\n\n url = f\"{base_url}/{version}/{endpoint}\"\n\n params_dict: dict[str, str] = {}\n\n if filter:\n params_dict[\"filter\"] = f\"filter={filter}\"\n if response_fields is not None:\n # If we have requested no response fields (e.g., in the case of --count) then just ask for IDs\n if len(response_fields) == 0:\n params_dict[\"response_fields\"] = \"response_fields=id\"\n else:\n params_dict[\"response_fields\"] = (\n f'response_fields={\",\".join(response_fields)}'\n )\n\n if page_limit:\n params_dict[\"page_limit\"] = f\"page_limit={page_limit}\"\n if sort:\n params_dict[\"sort\"] = f\"sort={sort}\"\n\n if other_params:\n for p in other_params:\n params_dict[p] = f\"{p}={other_params[p]}\"\n\n params = \"&\".join(p for p in params_dict.values() if p)\n if params:\n url += f\"?{params}\"\n\n return url\n\n def _check_filter(self, filter: str, endpoint: str) -> None:\n \"\"\"Passes the filter through [`LarkParser`][optimade.filterparser.LarkParser]\n from the optimade-python-tools reference server implementation.\n\n Parameters:\n filter: The filter string.\n endpoint: The endpoint being queried. If this endpoint is not \"known\" to\n OPTIMADE, the filter will automatically pass.\n\n Raises:\n RuntimeError: If the filter cannot be parsed.\n\n \"\"\"\n try:\n if endpoint in ENDPOINTS:\n parser = LarkParser()\n parser.parse(filter)\n except BadRequest as exc:\n self._progress.print(\n f\"[bold red]Filter [blue i]{filter!r}[/blue i] could not be parsed as an OPTIMADE filter.[/bold red]\",\n Panel(f\"[magenta]{exc}[/magenta]\"),\n )\n with silent_raise():\n raise RuntimeError(exc) from None\n\n def _handle_response(\n self, response: Union[httpx.Response, requests.Response], _task: TaskID\n ) -> tuple[dict[str, Any], str]:\n \"\"\"Handle the response from the server.\n\n Parameters:\n response: The response from the server.\n _task: The Rich TaskID for this task's progressbar.\n\n Returns:\n A dictionary containing the results, and a link to the next page,\n if it exists.\n\n \"\"\"\n\n # Handle error statuses\n if response.status_code == 429:\n raise TooManyRequestsException(response.content)\n if response.status_code != 200:\n try:\n errors = response.json().get(\"errors\")\n error_message = \"\\n\".join(\n [f\"{error['title']}: {error['detail']}\" for error in errors]\n )\n except Exception:\n error_message = str(response.content)\n\n raise RuntimeError(\n f\"{response.status_code} - {response.url}: {error_message}\"\n )\n\n try:\n r = response.json()\n except json.JSONDecodeError as exc:\n raise RuntimeError(\n f\"Could not decode response as JSON: {response.content!r}\"\n ) from exc\n\n # Accumulate results with correct empty containers if missing\n results = {\n \"data\": r.get(\"data\", []),\n \"meta\": r.get(\"meta\", {}),\n \"links\": r.get(\"links\", {}),\n \"included\": r.get(\"included\", []),\n \"errors\": r.get(\"errors\", []),\n }\n\n callback_response = None\n if self.callbacks:\n callback_response = self._execute_callbacks(results, response)\n callback_response = callback_response or {}\n\n # Advance the progress bar for this provider\n self._progress.update(\n _task,\n advance=callback_response.get(\"advance_results\", len(results[\"data\"])),\n total=results[\"meta\"].get(\"data_returned\", None),\n )\n\n next_url = callback_response.get(\"next\") or results[\"links\"].get(\"next\", None)\n if isinstance(next_url, dict):\n next_url = next_url.pop(\"href\")\n\n return results, next_url\n\n def _teardown(self, _task: TaskID, num_results: int) -> None:\n \"\"\"Update the finished status of the progress bar depending on the number of results.\n\n Parameters:\n _task: The Rich TaskID for this task's progressbar.\n num_results: The number of data entries returned.\n\n \"\"\"\n if num_results == 0:\n self._progress.update(_task, total=None, finished=False, complete=True)\n else:\n self._progress.update(\n _task, total=num_results, finished=True, complete=True\n )\n\n def _execute_callbacks(\n self, results: dict, response: Union[httpx.Response, requests.Response]\n ) -> Union[None, dict]:\n \"\"\"Execute any callbacks registered with the client.\n\n Parameters:\n results: The results from the query.\n response: The full response from the server.\n\n Returns:\n Either `None` or the string value returned from the *final* callback.\n\n \"\"\"\n request_url = str(response.request.url)\n if not self.callbacks:\n return None\n for callback in self.callbacks:\n cb_response = callback(request_url, results)\n return cb_response\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__current_endpoint","title":"__current_endpoint: Optional[str] = None
class-attribute
instance-attribute
","text":"Used internally when querying via client.structures.get()
to set the chosen endpoint. Should be reset to None
outside of all get()
calls.
__strict_async: bool = False
class-attribute
instance-attribute
","text":"Whether or not to fallover if use_async
is true yet asynchronous mode is impossible due to, e.g., a running event loop.
all_results: dict[str, dict[str, dict[str, QueryResults]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the results from each base URL for that particular filter.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.base_urls","title":"base_urls: Union[str, Iterable[str]]
instance-attribute
","text":"A list (or any iterable) of OPTIMADE base URLs to query.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.callbacks","title":"callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = callbacks
class-attribute
instance-attribute
","text":"A list of callbacks to execute after each successful request, used to e.g., write to a file, add results to a database or perform additional filtering.
The callbacks will receive the request URL and the results extracted from the JSON response, with keys 'data', 'meta', 'links', 'errors' and 'included'.
Each callback can return a dictionary that can modify the next_url
with the key next
and the progress bar with the key advance_results
. In the case of multiple provided callbacks, only the value returned by the final callback in the stack will be used.
count_binary_search: bool = True
class-attribute
instance-attribute
","text":"Enable binary search count for databases that do not support meta->data_returned
.
count_results: dict[str, dict[str, dict[str, int]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the number of results from each base URL for that particular filter.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.headers","title":"headers: dict = {'User-Agent': f'optimade-python-tools/{__version__}'}
class-attribute
instance-attribute
","text":"Additional HTTP headers.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.http_timeout","title":"http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0)
class-attribute
instance-attribute
","text":"The timeout to use for each HTTP request.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.max_attempts","title":"max_attempts: int = max_attempts
instance-attribute
","text":"The maximum number of times to repeat a failed query before giving up.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.max_results_per_provider","title":"max_results_per_provider: Optional[int] = max_results_per_provider
class-attribute
instance-attribute
","text":"Maximum number of results to downlod per provider. If None, will download all.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.property_lists","title":"property_lists: dict[str, dict[str, list[str]]] = defaultdict(dict)
class-attribute
instance-attribute
","text":"A dictionary containing list of properties served by each database, broken down by entry type, then database.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.silent","title":"silent: bool = silent
instance-attribute
","text":"Whether to disable progress bar printing.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.skip_ssl","title":"skip_ssl: bool = skip_ssl
class-attribute
instance-attribute
","text":"Whether to skip SSL verification.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.use_async","title":"use_async: bool = use_async
instance-attribute
","text":"Whether or not to make all requests asynchronously using asyncio.
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__getattribute__","title":"__getattribute__(name)
","text":"Allows entry endpoints to be queried via attribute access, using the allowed list for this module.
Should also pass through any extensions/<example>
endpoints.
Any non-entry-endpoint name requested will be passed to the original __getattribute__
.
Example
from optimade.client import OptimadeClient\ncli = OptimadeClient()\nstructures = cli.structures.get()\nreferences = cli.references.get()\ninfo_structures = cli.info.structures.get()\n
Source code in optimade/client/client.py
def __getattribute__(self, name):\n \"\"\"Allows entry endpoints to be queried via attribute access, using the\n allowed list for this module.\n\n Should also pass through any `extensions/<example>` endpoints.\n\n Any non-entry-endpoint name requested will be passed to the\n original `__getattribute__`.\n\n !!! example\n ```python\n from optimade.client import OptimadeClient\n cli = OptimadeClient()\n structures = cli.structures.get()\n references = cli.references.get()\n info_structures = cli.info.structures.get()\n ```\n\n \"\"\"\n if name in ENDPOINTS:\n if self.__current_endpoint == \"info\":\n self.__current_endpoint = f\"info/{name}\"\n elif self.__current_endpoint == \"extensions\":\n self.__current_endpoint = f\"extensions/{name}\"\n else:\n self.__current_endpoint = name\n return self\n\n return super().__getattribute__(name)\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.__init__","title":"__init__(base_urls=None, max_results_per_provider=1000, headers=None, http_timeout=None, max_attempts=5, use_async=True, silent=False, exclude_providers=None, include_providers=None, exclude_databases=None, http_client=None, verbosity=0, callbacks=None, skip_ssl=False)
","text":"Create the OPTIMADE client object.
Parameters:
Name Type Description Defaultbase_urls
Optional[Union[str, Iterable[str]]]
A list of OPTIMADE base URLs to query.
None
max_results_per_provider
int
The maximum number of results to download from each provider (-1 or 0 indicate unlimited).
1000
headers
Optional[dict]
Any additional HTTP headers to use for the queries.
None
http_timeout
Optional[Union[Timeout, float]]
The timeout to use per request. Defaults to 10 seconds with 1000 seconds for reads specifically. Overriding this value will replace all timeouts (connect, read, write and pool) with this value.
None
max_attempts
int
The maximum number of times to repeat a failing query.
5
use_async
bool
Whether or not to make all requests asynchronously.
True
exclude_providers
Optional[list[str]]
A set or collection of provider IDs to exclude from queries.
None
include_providers
Optional[list[str]]
A set or collection of provider IDs to include in queries.
None
exclude_databases
Optional[list[str]]
A set or collection of child database URLs to exclude from queries.
None
http_client
Optional[Union[type[AsyncClient], type[Session]]]
An override for the underlying HTTP client, primarily used for testing.
None
callbacks
Optional[list[Callable[[str, dict], Union[None, dict]]]]
A list of functions to call after each successful response, see the attribute OptimadeClient.callbacks
docstring for more details.
None
verbosity
int
The verbosity level of the client.
0
Source code in optimade/client/client.py
def __init__(\n self,\n base_urls: Optional[Union[str, Iterable[str]]] = None,\n max_results_per_provider: int = 1000,\n headers: Optional[dict] = None,\n http_timeout: Optional[Union[httpx.Timeout, float]] = None,\n max_attempts: int = 5,\n use_async: bool = True,\n silent: bool = False,\n exclude_providers: Optional[list[str]] = None,\n include_providers: Optional[list[str]] = None,\n exclude_databases: Optional[list[str]] = None,\n http_client: Optional[\n Union[type[httpx.AsyncClient], type[requests.Session]]\n ] = None,\n verbosity: int = 0,\n callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None,\n skip_ssl: bool = False,\n):\n \"\"\"Create the OPTIMADE client object.\n\n Parameters:\n base_urls: A list of OPTIMADE base URLs to query.\n max_results_per_provider: The maximum number of results to download\n from each provider (-1 or 0 indicate unlimited).\n headers: Any additional HTTP headers to use for the queries.\n http_timeout: The timeout to use per request. Defaults to 10\n seconds with 1000 seconds for reads specifically. Overriding this value\n will replace all timeouts (connect, read, write and pool) with this value.\n max_attempts: The maximum number of times to repeat a failing query.\n use_async: Whether or not to make all requests asynchronously.\n exclude_providers: A set or collection of provider IDs to exclude from queries.\n include_providers: A set or collection of provider IDs to include in queries.\n exclude_databases: A set or collection of child database URLs to exclude from queries.\n http_client: An override for the underlying HTTP client, primarily used for testing.\n callbacks: A list of functions to call after each successful response, see the\n attribute [`OptimadeClient.callbacks`][optimade.client.OptimadeClient.callbacks]\n docstring for more details.\n verbosity: The verbosity level of the client.\n\n \"\"\"\n\n self.max_results_per_provider = max_results_per_provider\n if self.max_results_per_provider in (-1, 0):\n self.max_results_per_provider = None\n\n self._excluded_providers = set(exclude_providers) if exclude_providers else None\n self._included_providers = set(include_providers) if include_providers else None\n self._excluded_databases = set(exclude_databases) if exclude_databases else None\n\n self.max_attempts = max_attempts\n self.silent = silent\n self.verbosity = verbosity\n self.skip_ssl = skip_ssl\n\n if headers:\n self.headers.update(headers)\n\n if not base_urls:\n progress = None\n if not self.silent:\n progress = OptimadeClientProgress()\n self.base_urls = list(\n get_all_databases(\n exclude_providers=self._excluded_providers,\n include_providers=self._included_providers,\n exclude_databases=self._excluded_databases,\n progress=progress,\n skip_ssl=self.skip_ssl,\n )\n )\n else:\n if exclude_providers or include_providers or exclude_databases:\n raise RuntimeError(\n \"Cannot provide both a list of base URLs and included/excluded databases.\"\n )\n\n self.base_urls = base_urls\n\n if isinstance(self.base_urls, str):\n self.base_urls = [self.base_urls]\n self.base_urls = list(self.base_urls)\n\n if not self.base_urls:\n raise SystemExit(\n \"Unable to access any OPTIMADE base URLs. If you believe this is an error, try manually specifying some base URLs.\"\n )\n\n if http_timeout:\n if isinstance(http_timeout, httpx.Timeout):\n self.http_timeout = http_timeout\n else:\n self.http_timeout = httpx.Timeout(http_timeout)\n\n self.use_async = use_async\n\n if http_client:\n self._http_client = http_client\n if issubclass(self._http_client, httpx.AsyncClient):\n if not self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use synchronous mode with an asynchronous HTTP client, please set `use_async=True` or pass an asynchronous HTTP client.\"\n )\n self.use_async = True\n elif issubclass(self._http_client, requests.Session):\n if self.use_async and self.__strict_async:\n raise RuntimeError(\n \"Cannot use async mode with a synchronous HTTP client, please set `use_async=False` or pass an synchronous HTTP client.\"\n )\n self.use_async = False\n else:\n if use_async:\n self._http_client = httpx.AsyncClient\n else:\n self._http_client = requests.Session\n\n self.callbacks = callbacks\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.binary_search_count","title":"binary_search_count(filter, endpoint, base_url, results=None)
","text":"In cases where data_returned
is not available (due to database limitations or otherwise), iteratively probe the final page of results available for a filter using binary search.
Note: These queries always happen synchronously across APIs, but can be executed asynchronously within a single API.
Parameters:
Name Type Description Defaultfilter
str
The OPTIMADE filter string for the query.
requiredendpoint
str
The endpoint to query.
requiredbase_url
str
The base URL to query.
requiredresults
Optional[dict]
The results from a previous query for the first page of results.
None
Returns:
Type Descriptionint
The number of results for the filter.
Source code inoptimade/client/client.py
def binary_search_count(\n self, filter: str, endpoint: str, base_url: str, results: Optional[dict] = None\n) -> int:\n \"\"\"In cases where `data_returned` is not available (due to database limitations or\n otherwise), iteratively probe the final page of results available for a filter using\n binary search.\n\n Note: These queries always happen synchronously across APIs, but can be executed\n asynchronously within a single API.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n base_url: The base URL to query.\n results: The results from a previous query for the first page of results.\n\n Returns:\n The number of results for the filter.\n\n \"\"\"\n if self.verbosity:\n self._progress.print(f\"Performing binary search count for {base_url}\")\n if self.use_async:\n return self._binary_search_count_async(filter, endpoint, base_url, results)\n\n else:\n raise NotImplementedError(\n \"Binary search count is not yet implemented for synchronous queries.\"\n )\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.count","title":"count(filter=None, endpoint=None)
","text":"Counts the number of results for the filter, requiring only 1 request per provider by making use of the meta->data_returned
key. If missing, attempts will be made to perform an exponential/binary search over pagination to count the results.
Raises:
Type DescriptionRuntimeError
If the query could not be executed.
Returns:
Type Descriptiondict[str, dict[str, dict[str, Optional[int]]]]
A nested mapping from endpoint, filter and base URL to the number of query results.
Source code inoptimade/client/client.py
def count(\n self, filter: Optional[str] = None, endpoint: Optional[str] = None\n) -> dict[str, dict[str, dict[str, Optional[int]]]]:\n \"\"\"Counts the number of results for the filter, requiring\n only 1 request per provider by making use of the `meta->data_returned`\n key. If missing, attempts will be made to perform an exponential/binary\n search over pagination to count the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the number of query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Counting results for [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n page_limit=1,\n paginate=False,\n response_fields=[],\n sort=None,\n )\n count_results = {}\n\n for base_url in results:\n count_results[base_url] = results[base_url].meta.get(\n \"data_returned\", None\n )\n\n if count_results[base_url] is None or self._force_binary_search:\n if self.count_binary_search:\n count_results[base_url] = self.binary_search_count(\n filter, endpoint, base_url, results\n )\n else:\n self._progress.print(\n f\"Warning: {base_url} did not return a value for `meta->data_returned`, unable to count results. Full response: {results[base_url]}\"\n )\n\n self.count_results[endpoint][filter] = count_results\n return {endpoint: {filter: count_results}}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get","title":"get(filter=None, endpoint=None, response_fields=None, sort=None)
","text":"Gets the results from the endpoint and filter across the defined OPTIMADE APIs.
Parameters:
Name Type Description Defaultfilter
Optional[str]
The OPTIMADE filter string for the query.
None
endpoint
Optional[str]
The endpoint to query.
None
response_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
Raises:
Type DescriptionRuntimeError
If the query could not be executed.
Returns:
Type Descriptiondict[str, dict[str, dict[str, dict]]]
A nested mapping from endpoint, filter and base URL to the query results.
Source code inoptimade/client/client.py
def get(\n self,\n filter: Optional[str] = None,\n endpoint: Optional[str] = None,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n) -> dict[str, dict[str, dict[str, dict]]]:\n \"\"\"Gets the results from the endpoint and filter across the\n defined OPTIMADE APIs.\n\n Parameters:\n filter: The OPTIMADE filter string for the query.\n endpoint: The endpoint to query.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n\n Raises:\n RuntimeError: If the query could not be executed.\n\n Returns:\n A nested mapping from endpoint, filter and base URL to the query results.\n\n \"\"\"\n\n if endpoint is None:\n if self.__current_endpoint is not None:\n endpoint = self.__current_endpoint\n self.__current_endpoint = None\n else:\n endpoint = \"structures\"\n\n if filter is None:\n filter = \"\"\n\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n self._check_filter(filter, endpoint)\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Performing query [bold yellow]{endpoint}[/bold yellow]/?filter=[bold magenta][i]{filter}[/i][/bold magenta]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n filter,\n endpoint,\n response_fields=response_fields,\n page_limit=None,\n paginate=True,\n sort=sort,\n )\n self.all_results[endpoint][filter] = results\n return {endpoint: {filter: {k: results[k].asdict() for k in results}}}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get_one","title":"get_one(endpoint, filter, base_url, response_fields=None, sort=None, page_limit=None, paginate=True, other_params=None)
","text":"Executes the query synchronously on one API.
Parameters:
Name Type Description Defaultendpoint
str
The OPTIMADE endpoint to query.
requiredfilter
str
The OPTIMADE filter string.
requiredresponse_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
page_limit
Optional[int]
A page limit to enforce for each query (used in conjunction with paginate
).
None
paginate
bool
Whether to pull all pages of results (up to the value of max_results_per_provider
) or whether to return after one page.
True
other_params
Optional[dict[str, Any]]
Any other parameters to pass to the server.
None
Returns:
Type Descriptiondict[str, QueryResults]
A dictionary mapping from base URL to the results of the query.
Source code inoptimade/client/client.py
def get_one(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n) -> dict[str, QueryResults]:\n \"\"\"Executes the query synchronously on one API.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return self._get_one(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{exc}[/red i]\"\n )\n return {base_url: error_query_results}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.get_one_async","title":"get_one_async(endpoint, filter, base_url, response_fields=None, sort=None, page_limit=None, paginate=True, other_params=None)
async
","text":"Executes the query asynchronously on one API.
Note
This method currently makes non-blocking requests to a single API, but these requests are executed serially on that API, i.e., results are pulled one page at a time, but requests will not block other async requests to other APIs.
Parameters:
Name Type Description Defaultendpoint
str
The OPTIMADE endpoint to query.
requiredfilter
str
The OPTIMADE filter string.
requiredresponse_fields
Optional[list[str]]
A list of response fields to request from the server.
None
sort
Optional[str]
The field by which to sort the results.
None
page_limit
Optional[int]
A page limit to enforce for each query (used in conjunction with paginate
).
None
paginate
bool
Whether to pull all pages of results (up to the value of max_results_per_provider
) or whether to return after one page.
True
other_params
Optional[dict[str, Any]]
Any other parameters to pass to the server.
None
Returns:
Type Descriptiondict[str, QueryResults]
A dictionary mapping from base URL to the results of the query.
Source code inoptimade/client/client.py
async def get_one_async(\n self,\n endpoint: str,\n filter: str,\n base_url: str,\n response_fields: Optional[list[str]] = None,\n sort: Optional[str] = None,\n page_limit: Optional[int] = None,\n paginate: bool = True,\n other_params: Optional[dict[str, Any]] = None,\n) -> dict[str, QueryResults]:\n \"\"\"Executes the query asynchronously on one API.\n\n !!! note\n This method currently makes non-blocking requests\n to a single API, but these requests are executed\n serially on that API, i.e., results are pulled one\n page at a time, but requests will not block other\n async requests to other APIs.\n\n Parameters:\n endpoint: The OPTIMADE endpoint to query.\n filter: The OPTIMADE filter string.\n response_fields: A list of response fields to request\n from the server.\n sort: The field by which to sort the results.\n page_limit: A page limit to enforce for each query (used in\n conjunction with `paginate`).\n paginate: Whether to pull all pages of results (up to the\n value of `max_results_per_provider`) or whether to return\n after one page.\n other_params: Any other parameters to pass to the server.\n\n Returns:\n A dictionary mapping from base URL to the results of the query.\n\n \"\"\"\n try:\n return await self._get_one_async(\n endpoint,\n filter,\n base_url,\n page_limit=page_limit,\n paginate=paginate,\n response_fields=response_fields,\n sort=sort,\n other_params=other_params,\n )\n except Exception as exc:\n error_query_results = QueryResults()\n error_query_results.errors = [\n f\"{exc.__class__.__name__}: {str(exc.args[0])}\"\n ]\n self._progress.print(\n f\"[red]Error[/red]: Provider {str(base_url)!r} returned: [red i]{error_query_results.errors}[/red i]\"\n )\n return {base_url: error_query_results}\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.list_properties","title":"list_properties(entry_type)
","text":"Returns the list of properties reported at /info/<entry_type>
for the given entry type, for each database.
optimade/client/client.py
def list_properties(\n self,\n entry_type: str,\n) -> dict[str, list[str]]:\n \"\"\"Returns the list of properties reported at `/info/<entry_type>`\n for the given entry type, for each database.\n\n \"\"\"\n self._progress = OptimadeClientProgress()\n if self.silent:\n self._progress.disable = True\n\n with self._progress:\n if not self.silent:\n self._progress.print(\n Panel(\n f\"Listing properties for [bold yellow]{entry_type}[/bold yellow]\",\n expand=False,\n )\n )\n results = self._execute_queries(\n \"\",\n f\"info/{entry_type}\",\n paginate=False,\n page_limit=1,\n response_fields=[],\n sort=None,\n )\n self.property_lists = {entry_type: {}}\n for database in results:\n self.property_lists[entry_type][database] = list(\n results[database].data.get(\"properties\", {}).keys() # type: ignore\n )\n return self.property_lists[entry_type]\n
"},{"location":"api_reference/client/client/#optimade.client.client.OptimadeClient.search_property","title":"search_property(query, entry_type)
","text":"Searches for the query substring within the listed properties served by each database.
Parameters:
Name Type Description Defaultquery
str
The substring to search for.
requiredentry_type
str
The entry type to query.
requiredReturns:
Type Descriptiondict[str, list[str]]
A nested dictionary of matching property lists, arranged by
dict[str, list[str]]
entry type and database.
Source code inoptimade/client/client.py
def search_property(self, query: str, entry_type: str) -> dict[str, list[str]]:\n \"\"\"Searches for the query substring within the listed properties\n served by each database.\n\n Parameters:\n query: The substring to search for.\n entry_type: The entry type to query.\n\n Returns:\n A nested dictionary of matching property lists, arranged by\n entry type and database.\n\n \"\"\"\n if not self.property_lists:\n self.list_properties(entry_type=entry_type)\n\n matching_properties: dict[str, dict[str, list[str]]] = {\n entry_type: defaultdict(list)\n }\n if entry_type in self.property_lists:\n for database in self.property_lists[entry_type]:\n for property in self.property_lists[entry_type][database]:\n if query in property:\n matching_properties[entry_type][database].append(property)\n return matching_properties[entry_type]\n
"},{"location":"api_reference/client/utils/","title":"utils","text":""},{"location":"api_reference/client/utils/#optimade.client.utils.OptimadeClientProgress","title":"OptimadeClientProgress
","text":" Bases: Progress
A wrapper around Rich.Progress
that defines the OPTIMADE client progressbars.
optimade/client/utils.py
class OptimadeClientProgress(Progress):\n \"\"\"A wrapper around `Rich.Progress` that defines the OPTIMADE client progressbars.\"\"\"\n\n def __init__(self):\n super().__init__(\n SpinnerColumn(finished_text=\"[green]\u2713\"),\n TextColumn(\"[progress.description]{task.description}\"),\n BarColumn(),\n TaskProgressColumn(\n text_format=\"[progress.completed]{task.completed}/[progress.total]{task.total}\",\n text_format_no_percentage=\"[progress.completed]{task.completed}\",\n ),\n TimeElapsedColumn(),\n console=Console(stderr=True),\n auto_refresh=True,\n refresh_per_second=10,\n )\n\n def print(self, *args, **kwargs):\n if not self.disable:\n super().print(*args, **kwargs)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.QueryResults","title":"QueryResults
dataclass
","text":"A container dataclass for the results from a given query.
Source code inoptimade/client/utils.py
@dataclass\nclass QueryResults:\n \"\"\"A container dataclass for the results from a given query.\"\"\"\n\n data: Union[dict, list[dict]] = field(default_factory=list, init=False) # type: ignore[assignment]\n errors: list[str] = field(default_factory=list, init=False)\n links: dict = field(default_factory=dict, init=False)\n included: list[dict] = field(default_factory=list, init=False)\n meta: dict = field(default_factory=dict, init=False)\n\n @property\n def included_index(self) -> set[str]:\n if not getattr(self, \"_included_index\", None):\n self._included_index: set[str] = set()\n return self._included_index\n\n def asdict(self):\n return asdict(self)\n\n def update(self, page_results: dict) -> None:\n \"\"\"Combine the results from one page with the existing results for a given query.\n\n Parameters:\n page_results: The results for the current page.\n\n \"\"\"\n\n if \"data\" in page_results:\n # If the `data` field is a list, add it to our existing results.\n # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)\n # and should be added as the only `data` field for these results.\n if isinstance(page_results[\"data\"], list):\n self.data.extend(page_results[\"data\"]) # type: ignore[union-attr]\n elif not self.data:\n self.data = page_results[\"data\"]\n else:\n raise RuntimeError(\n \"Not overwriting old `data` field in `QueryResults`.\"\n )\n\n if \"errors\" in page_results:\n self.errors.extend(page_results[\"errors\"])\n\n # Combine meta/links fields across all pages in a sensible way, i.e.,\n # if we really reached the last page of results, then make sure `links->next`\n # is null in the final response, and make sure `meta->more_data_available` is None or False.\n keys_to_filter = {\n \"links\": (\"next\", \"prev\"),\n \"meta\": (\"query\", \"more_data_available\"),\n }\n for top_level_key in keys_to_filter:\n if top_level_key not in page_results:\n page_results[top_level_key] = {}\n for k in keys_to_filter[top_level_key]:\n if k not in page_results[top_level_key]:\n page_results[top_level_key][k] = None\n getattr(self, top_level_key).update(\n {k: page_results[top_level_key][k] for k in page_results[top_level_key]}\n )\n\n # Only add new unique entries to the included list\n for d in page_results.get(\"included\", []):\n typed_id = f\"{d['type']}/{d['id']}\"\n if typed_id not in self.included_index:\n self.included_index.add(typed_id)\n self.included.append(d)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.QueryResults.update","title":"update(page_results)
","text":"Combine the results from one page with the existing results for a given query.
Parameters:
Name Type Description Defaultpage_results
dict
The results for the current page.
required Source code inoptimade/client/utils.py
def update(self, page_results: dict) -> None:\n \"\"\"Combine the results from one page with the existing results for a given query.\n\n Parameters:\n page_results: The results for the current page.\n\n \"\"\"\n\n if \"data\" in page_results:\n # If the `data` field is a list, add it to our existing results.\n # Otherwise, as is the case for `info` endpoints, `data` is a dictionary (or null)\n # and should be added as the only `data` field for these results.\n if isinstance(page_results[\"data\"], list):\n self.data.extend(page_results[\"data\"]) # type: ignore[union-attr]\n elif not self.data:\n self.data = page_results[\"data\"]\n else:\n raise RuntimeError(\n \"Not overwriting old `data` field in `QueryResults`.\"\n )\n\n if \"errors\" in page_results:\n self.errors.extend(page_results[\"errors\"])\n\n # Combine meta/links fields across all pages in a sensible way, i.e.,\n # if we really reached the last page of results, then make sure `links->next`\n # is null in the final response, and make sure `meta->more_data_available` is None or False.\n keys_to_filter = {\n \"links\": (\"next\", \"prev\"),\n \"meta\": (\"query\", \"more_data_available\"),\n }\n for top_level_key in keys_to_filter:\n if top_level_key not in page_results:\n page_results[top_level_key] = {}\n for k in keys_to_filter[top_level_key]:\n if k not in page_results[top_level_key]:\n page_results[top_level_key][k] = None\n getattr(self, top_level_key).update(\n {k: page_results[top_level_key][k] for k in page_results[top_level_key]}\n )\n\n # Only add new unique entries to the included list\n for d in page_results.get(\"included\", []):\n typed_id = f\"{d['type']}/{d['id']}\"\n if typed_id not in self.included_index:\n self.included_index.add(typed_id)\n self.included.append(d)\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.RecoverableHTTPError","title":"RecoverableHTTPError
","text":" Bases: Exception
Base class for any HTTP issues that may be recoverable by just repeating the query.
Source code inoptimade/client/utils.py
class RecoverableHTTPError(Exception):\n \"\"\"Base class for any HTTP issues that may be recoverable by just\n repeating the query.\"\"\"\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.TooManyRequestsException","title":"TooManyRequestsException
","text":" Bases: RecoverableHTTPError
For when the underlying HTTP request returns 429: Too Many Requests.
Source code inoptimade/client/utils.py
class TooManyRequestsException(RecoverableHTTPError):\n \"\"\"For when the underlying HTTP request returns 429: Too Many Requests.\"\"\"\n
"},{"location":"api_reference/client/utils/#optimade.client.utils.silent_raise","title":"silent_raise()
","text":"Raise an exception without printing a traceback, or the exception message itself.
Source code inoptimade/client/utils.py
@contextmanager\ndef silent_raise():\n \"\"\"Raise an exception without printing a traceback, or the exception message itself.\"\"\"\n default_value = getattr(\n sys, \"tracebacklimit\", 1000\n ) # `1000` is a Python's default value\n default_excepthook = getattr(sys, \"excepthook\")\n sys.tracebacklimit = 0\n sys.excepthook = lambda type, value, traceback: None\n yield\n sys.tracebacklimit = default_value # revert changes\n sys.excepthook = default_excepthook\n
"},{"location":"api_reference/filterparser/lark_parser/","title":"lark_parser","text":"This submodule implements the LarkParser
class, which uses the lark library to parse filter strings with a defined OPTIMADE filter grammar into Lark.Tree
objects for use by the filter transformers.
LarkParser
","text":"This class wraps a versioned OPTIMADE grammar and allows it to be parsed into Lark tree objects.
Source code inoptimade/filterparser/lark_parser.py
class LarkParser:\n \"\"\"This class wraps a versioned OPTIMADE grammar and allows\n it to be parsed into Lark tree objects.\n\n \"\"\"\n\n def __init__(\n self, version: Optional[tuple[int, int, int]] = None, variant: str = \"default\"\n ):\n \"\"\"For a given version and variant, try to load the corresponding grammar.\n\n Parameters:\n version: The grammar version number to use (e.g., `(1, 0, 1)` for v1.0.1).\n variant: The grammar variant to employ.\n\n Raises:\n ParserError: If the requested version/variant of the\n grammar does not exist.\n\n \"\"\"\n\n if not version:\n version = max(\n _ for _ in AVAILABLE_PARSERS if AVAILABLE_PARSERS[_].get(\"default\")\n )\n\n if version not in AVAILABLE_PARSERS:\n raise ParserError(f\"Unknown parser grammar version: {version}\")\n\n if variant not in AVAILABLE_PARSERS[version]:\n raise ParserError(f\"Unknown variant of the parser: {variant}\")\n\n self.version = version\n self.variant = variant\n\n with open(AVAILABLE_PARSERS[version][variant]) as f:\n self.lark = Lark(f, maybe_placeholders=False)\n\n self.tree: Optional[Tree] = None\n self.filter: Optional[str] = None\n\n def parse(self, filter_: str) -> Tree:\n \"\"\"Parse a filter string into a `lark.Tree`.\n\n Parameters:\n filter_: The filter string to parse.\n\n Raises:\n BadRequest: If the filter cannot be parsed.\n\n Returns:\n The parsed filter.\n\n \"\"\"\n try:\n self.tree = self.lark.parse(filter_)\n self.filter = filter_\n return self.tree\n except Exception as exc:\n raise BadRequest(\n detail=f\"Unable to parse filter {filter_}. Lark traceback: \\n{exc}\"\n ) from exc\n\n def __repr__(self):\n if isinstance(self.tree, Tree):\n return self.tree.pretty()\n return repr(self.lark)\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.LarkParser.__init__","title":"__init__(version=None, variant='default')
","text":"For a given version and variant, try to load the corresponding grammar.
Parameters:
Name Type Description Defaultversion
Optional[tuple[int, int, int]]
The grammar version number to use (e.g., (1, 0, 1)
for v1.0.1).
None
variant
str
The grammar variant to employ.
'default'
Raises:
Type DescriptionParserError
If the requested version/variant of the grammar does not exist.
Source code inoptimade/filterparser/lark_parser.py
def __init__(\n self, version: Optional[tuple[int, int, int]] = None, variant: str = \"default\"\n):\n \"\"\"For a given version and variant, try to load the corresponding grammar.\n\n Parameters:\n version: The grammar version number to use (e.g., `(1, 0, 1)` for v1.0.1).\n variant: The grammar variant to employ.\n\n Raises:\n ParserError: If the requested version/variant of the\n grammar does not exist.\n\n \"\"\"\n\n if not version:\n version = max(\n _ for _ in AVAILABLE_PARSERS if AVAILABLE_PARSERS[_].get(\"default\")\n )\n\n if version not in AVAILABLE_PARSERS:\n raise ParserError(f\"Unknown parser grammar version: {version}\")\n\n if variant not in AVAILABLE_PARSERS[version]:\n raise ParserError(f\"Unknown variant of the parser: {variant}\")\n\n self.version = version\n self.variant = variant\n\n with open(AVAILABLE_PARSERS[version][variant]) as f:\n self.lark = Lark(f, maybe_placeholders=False)\n\n self.tree: Optional[Tree] = None\n self.filter: Optional[str] = None\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.LarkParser.parse","title":"parse(filter_)
","text":"Parse a filter string into a lark.Tree
.
Parameters:
Name Type Description Defaultfilter_
str
The filter string to parse.
requiredRaises:
Type DescriptionBadRequest
If the filter cannot be parsed.
Returns:
Type DescriptionTree
The parsed filter.
Source code inoptimade/filterparser/lark_parser.py
def parse(self, filter_: str) -> Tree:\n \"\"\"Parse a filter string into a `lark.Tree`.\n\n Parameters:\n filter_: The filter string to parse.\n\n Raises:\n BadRequest: If the filter cannot be parsed.\n\n Returns:\n The parsed filter.\n\n \"\"\"\n try:\n self.tree = self.lark.parse(filter_)\n self.filter = filter_\n return self.tree\n except Exception as exc:\n raise BadRequest(\n detail=f\"Unable to parse filter {filter_}. Lark traceback: \\n{exc}\"\n ) from exc\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.ParserError","title":"ParserError
","text":" Bases: Exception
Triggered by critical parsing errors that should lead to 500 Server Error HTTP statuses.
Source code inoptimade/filterparser/lark_parser.py
class ParserError(Exception):\n \"\"\"Triggered by critical parsing errors that should lead\n to 500 Server Error HTTP statuses.\n \"\"\"\n
"},{"location":"api_reference/filterparser/lark_parser/#optimade.filterparser.lark_parser.get_versions","title":"get_versions()
","text":"Find grammar files within this package's grammar directory, returning a dictionary broken down by scraped grammar version (major, minor, patch) and variant (a string tag).
Returns:
Type Descriptiondict[tuple[int, int, int], dict[str, Path]]
A mapping from version, variant to grammar file name.
Source code inoptimade/filterparser/lark_parser.py
def get_versions() -> dict[tuple[int, int, int], dict[str, Path]]:\n \"\"\"Find grammar files within this package's grammar directory,\n returning a dictionary broken down by scraped grammar version\n (major, minor, patch) and variant (a string tag).\n\n Returns:\n A mapping from version, variant to grammar file name.\n\n \"\"\"\n dct: dict[tuple[int, int, int], dict[str, Path]] = {}\n for filename in Path(__file__).parent.joinpath(\"../grammar\").glob(\"*.lark\"):\n tags = filename.stem.lstrip(\"v\").split(\".\")\n version: tuple[int, int, int] = (int(tags[0]), int(tags[1]), int(tags[2]))\n variant: str = \"default\" if len(tags) == 3 else str(tags[-1])\n if version not in dct:\n dct[version] = {}\n dct[version][variant] = filename\n return dct\n
"},{"location":"api_reference/filtertransformers/base_transformer/","title":"base_transformer","text":"This submodule implements the BaseTransformer
and Quantity
classes for turning filters parsed by lark into backend-specific queries.
BaseTransformer
","text":" Bases: Transformer
, ABC
Generic filter transformer that handles various parts of the grammar in a backend non-specific way.
Attributes:
Name Type Descriptionoperator_map
dict[str, Optional[str]]
A map from comparison operators to their backend-specific versions.
mapper
Optional[type[BaseResourceMapper]]
A resource mapper object that defines the expected fields and acts as a container for various field-related configuration.
Source code inoptimade/filtertransformers/base_transformer.py
class BaseTransformer(Transformer, abc.ABC):\n \"\"\"Generic filter transformer that handles various\n parts of the grammar in a backend non-specific way.\n\n Attributes:\n operator_map: A map from comparison operators\n to their backend-specific versions.\n mapper: A resource mapper object that defines the\n expected fields and acts as a container for\n various field-related configuration.\n\n \"\"\"\n\n mapper: Optional[type[BaseResourceMapper]] = None\n operator_map: dict[str, Optional[str]] = {\n \"<\": None,\n \"<=\": None,\n \">\": None,\n \">=\": None,\n \"!=\": None,\n \"=\": None,\n }\n\n # map from operators to their syntactic (as opposed to logical) inverse to handle\n # equivalence between cases like \"A > 3\" and \"3 < A\".\n _reversed_operator_map = {\n \">\": \"<\",\n \">=\": \"<=\",\n \"<\": \">\",\n \"<=\": \">=\",\n \"=\": \"=\",\n \"!=\": \"!=\",\n }\n\n _quantity_type: type[Quantity] = Quantity\n _quantities = None\n\n def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n\n @property\n def backend_mapping(self) -> dict[str, Quantity]:\n \"\"\"A mapping between backend field names (aliases) and the corresponding\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] object.\n \"\"\"\n return {\n quantity.backend_field: quantity # type: ignore[misc]\n for _, quantity in self.quantities.items()\n }\n\n @property\n def quantities(self) -> dict[str, Quantity]:\n \"\"\"A mapping from the OPTIMADE field name to the corresponding\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects.\n \"\"\"\n if self._quantities is None:\n self._quantities = self._build_quantities()\n\n return self._quantities\n\n @quantities.setter\n def quantities(self, quantities: dict[str, Quantity]) -> None:\n self._quantities = quantities\n\n def _build_quantities(self) -> dict[str, Quantity]:\n \"\"\"Creates a dictionary of field names mapped to\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects from the\n fields registered by the mapper.\n\n \"\"\"\n\n quantities = {}\n\n if self.mapper is not None:\n for field in self.mapper.ALL_ATTRIBUTES:\n alias = self.mapper.get_backend_field(field)\n # Allow length aliases to be defined relative to either backend fields or OPTIMADE fields,\n # with preference for those defined from OPTIMADE fields\n length_alias = self.mapper.length_alias_for(\n field\n ) or self.mapper.length_alias_for(alias)\n\n if field not in quantities:\n quantities[field] = self._quantity_type(\n name=field, backend_field=alias\n )\n\n if length_alias:\n if length_alias not in quantities:\n quantities[length_alias] = self._quantity_type(\n name=length_alias,\n backend_field=self.mapper.get_backend_field(length_alias),\n )\n quantities[field].length_quantity = quantities[length_alias]\n\n return quantities\n\n def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n\n def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n\n def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n\n def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n\n @v_args(inline=True)\n def constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n\n @v_args(inline=True)\n def not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n\n def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n\n @v_args(inline=True)\n def string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n\n @v_args(inline=True)\n def signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n\n @v_args(inline=True)\n def number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n\n @v_args(inline=True)\n def comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n\n def value_list(self, arg):\n \"\"\"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\"\"\"\n\n def value_zip(self, arg):\n \"\"\"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\"\"\"\n pass\n\n def value_zip_list(self, arg):\n \"\"\"value_zip_list: value_zip ( \",\" value_zip )*\"\"\"\n\n def expression(self, arg):\n \"\"\"expression: expression_clause ( OR expression_clause )\"\"\"\n\n def expression_clause(self, arg):\n \"\"\"expression_clause: expression_phrase ( AND expression_phrase )*\"\"\"\n\n def expression_phrase(self, arg):\n \"\"\"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\"\"\"\n\n def property_first_comparison(self, arg):\n \"\"\"property_first_comparison:\n property ( value_op_rhs\n | known_op_rhs\n | fuzzy_string_op_rhs\n | set_op_rhs\n | set_zip_op_rhs\n | length_op_rhs )\n\n \"\"\"\n\n def constant_first_comparison(self, arg):\n \"\"\"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\"\"\"\n\n @v_args(inline=True)\n def value_op_rhs(self, operator, value):\n \"\"\"value_op_rhs: OPERATOR value\"\"\"\n\n def known_op_rhs(self, arg):\n \"\"\"known_op_rhs: IS ( KNOWN | UNKNOWN )\"\"\"\n\n def fuzzy_string_op_rhs(self, arg):\n \"\"\"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\"\"\"\n\n def set_op_rhs(self, arg):\n \"\"\"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\"\"\"\n\n def length_op_rhs(self, arg):\n \"\"\"length_op_rhs: LENGTH [ OPERATOR ] value\"\"\"\n\n def set_zip_op_rhs(self, arg):\n \"\"\"set_zip_op_rhs: property_zip_addon HAS ( value_zip\n | ONLY value_zip_list\n | ALL value_zip_list\n | ANY value_zip_list )\n\n \"\"\"\n\n def property_zip_addon(self, arg):\n \"\"\"property_zip_addon: \":\" property (\":\" property)*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
quantities: dict[str, Quantity]
property
writable
","text":"A mapping from the OPTIMADE field name to the corresponding Quantity
objects.
__default__(data, children, meta)
","text":"The default rule to call when no definition is found for a particular construct.
Source code inoptimade/filtertransformers/base_transformer.py
def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.__init__","title":"__init__(mapper=None)
","text":"Initialise the transformer object, optionally loading in a resource mapper for use when post-processing.
Source code inoptimade/filtertransformers/base_transformer.py
def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.constant_first_comparison","title":"constant_first_comparison(arg)
","text":"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )
Source code inoptimade/filtertransformers/base_transformer.py
def constant_first_comparison(self, arg):\n \"\"\"constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression","title":"expression(arg)
","text":"expression: expression_clause ( OR expression_clause )
Source code inoptimade/filtertransformers/base_transformer.py
def expression(self, arg):\n \"\"\"expression: expression_clause ( OR expression_clause )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression_clause","title":"expression_clause(arg)
","text":"expression_clause: expression_phrase ( AND expression_phrase )*
Source code inoptimade/filtertransformers/base_transformer.py
def expression_clause(self, arg):\n \"\"\"expression_clause: expression_phrase ( AND expression_phrase )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.expression_phrase","title":"expression_phrase(arg)
","text":"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )
Source code inoptimade/filtertransformers/base_transformer.py
def expression_phrase(self, arg):\n \"\"\"expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.filter","title":"filter(arg)
","text":"filter: expression*
Source code inoptimade/filtertransformers/base_transformer.py
def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.fuzzy_string_op_rhs","title":"fuzzy_string_op_rhs(arg)
","text":"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value
Source code inoptimade/filtertransformers/base_transformer.py
def fuzzy_string_op_rhs(self, arg):\n \"\"\"fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.known_op_rhs","title":"known_op_rhs(arg)
","text":"known_op_rhs: IS ( KNOWN | UNKNOWN )
Source code inoptimade/filtertransformers/base_transformer.py
def known_op_rhs(self, arg):\n \"\"\"known_op_rhs: IS ( KNOWN | UNKNOWN )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.length_op_rhs","title":"length_op_rhs(arg)
","text":"length_op_rhs: LENGTH [ OPERATOR ] value
Source code inoptimade/filtertransformers/base_transformer.py
def length_op_rhs(self, arg):\n \"\"\"length_op_rhs: LENGTH [ OPERATOR ] value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.number","title":"number(number)
","text":"number: SIGNED_INT | SIGNED_FLOAT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.postprocess","title":"postprocess(query)
","text":"Post-process the query according to the rules defined for the backend, returning the backend-specific query.
Source code inoptimade/filtertransformers/base_transformer.py
def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property","title":"property(args)
","text":"property: IDENTIFIER ( \".\" IDENTIFIER )*
If this transformer has an associated mapper, the property will be compared to possible relationship entry types and for any supported provider prefixes. If there is a match, this rule will return a string and not a dereferenced Quantity
.
Raises:
Type DescriptionBadRequest
If the property does not match any of the above rules.
Source code inoptimade/filtertransformers/base_transformer.py
def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property_first_comparison","title":"property_first_comparison(arg)
","text":"property_first_comparison: property ( value_op_rhs | known_op_rhs | fuzzy_string_op_rhs | set_op_rhs | set_zip_op_rhs | length_op_rhs )
Source code inoptimade/filtertransformers/base_transformer.py
def property_first_comparison(self, arg):\n \"\"\"property_first_comparison:\n property ( value_op_rhs\n | known_op_rhs\n | fuzzy_string_op_rhs\n | set_op_rhs\n | set_zip_op_rhs\n | length_op_rhs )\n\n \"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.property_zip_addon","title":"property_zip_addon(arg)
","text":"property_zip_addon: \":\" property (\":\" property)*
Source code inoptimade/filtertransformers/base_transformer.py
def property_zip_addon(self, arg):\n \"\"\"property_zip_addon: \":\" property (\":\" property)*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.set_op_rhs","title":"set_op_rhs(arg)
","text":"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )
Source code inoptimade/filtertransformers/base_transformer.py
def set_op_rhs(self, arg):\n \"\"\"set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.set_zip_op_rhs","title":"set_zip_op_rhs(arg)
","text":"set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )
Source code inoptimade/filtertransformers/base_transformer.py
def set_zip_op_rhs(self, arg):\n \"\"\"set_zip_op_rhs: property_zip_addon HAS ( value_zip\n | ONLY value_zip_list\n | ALL value_zip_list\n | ANY value_zip_list )\n\n \"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.signed_int","title":"signed_int(number)
","text":"signed_int : SIGNED_INT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.string","title":"string(string)
","text":"string: ESCAPED_STRING
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_list","title":"value_list(arg)
","text":"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*
Source code inoptimade/filtertransformers/base_transformer.py
def value_list(self, arg):\n \"\"\"value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_op_rhs","title":"value_op_rhs(operator, value)
","text":"value_op_rhs: OPERATOR value
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value_op_rhs(self, operator, value):\n \"\"\"value_op_rhs: OPERATOR value\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_zip","title":"value_zip(arg)
","text":"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*
Source code inoptimade/filtertransformers/base_transformer.py
def value_zip(self, arg):\n \"\"\"value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\"\"\"\n pass\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.BaseTransformer.value_zip_list","title":"value_zip_list(arg)
","text":"value_zip_list: value_zip ( \",\" value_zip )*
Source code inoptimade/filtertransformers/base_transformer.py
def value_zip_list(self, arg):\n \"\"\"value_zip_list: value_zip ( \",\" value_zip )*\"\"\"\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.Quantity","title":"Quantity
","text":"Class to provide information about available quantities to the transformer.
The transformer can use Quantity
's to
Attributes:
Name Type Descriptionname
str
The name of the quantity as used in the filter expressions.
backend_field
Optional[str]
The name of the field for this quantity in the backend database, will be name
by default.
length_quantity
Optional[Quantity]
Another (typically integer) Quantity
that can be queried as the length of this quantity, e.g. elements
and nelements
. Backends can then decide whether to use this for all \"LENGTH\" queries.
optimade/filtertransformers/base_transformer.py
class Quantity:\n \"\"\"Class to provide information about available quantities to the transformer.\n\n The transformer can use [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]'s to\n\n * do some semantic checks,\n * map quantities to the underlying backend field name.\n\n Attributes:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n name: str\n backend_field: Optional[str]\n length_quantity: Optional[\"Quantity\"]\n\n def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"Quantity\"] = None,\n ):\n \"\"\"Initialise the `quantity` from it's name and aliases.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n self.name = name\n self.backend_field = backend_field if backend_field is not None else name\n self.length_quantity = length_quantity\n
"},{"location":"api_reference/filtertransformers/base_transformer/#optimade.filtertransformers.base_transformer.Quantity.__init__","title":"__init__(name, backend_field=None, length_quantity=None)
","text":"Initialise the quantity
from it's name and aliases.
Parameters:
Name Type Description Defaultname
str
The name of the quantity as used in the filter expressions.
requiredbackend_field
Optional[str]
The name of the field for this quantity in the backend database, will be name
by default.
None
length_quantity
Optional[Quantity]
Another (typically integer) Quantity
that can be queried as the length of this quantity, e.g. elements
and nelements
. Backends can then decide whether to use this for all \"LENGTH\" queries.
None
Source code in optimade/filtertransformers/base_transformer.py
def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"Quantity\"] = None,\n):\n \"\"\"Initialise the `quantity` from it's name and aliases.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in the backend database, will be\n `name` by default.\n length_quantity: Another (typically integer) [`Quantity`][optimade.filtertransformers.base_transformer.Quantity]\n that can be queried as the length of this quantity, e.g. `elements` and `nelements`. Backends\n can then decide whether to use this for all \"LENGTH\" queries.\n\n \"\"\"\n\n self.name = name\n self.backend_field = backend_field if backend_field is not None else name\n self.length_quantity = length_quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/","title":"elasticsearch","text":""},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer","title":"ElasticTransformer
","text":" Bases: BaseTransformer
Transformer that transforms v0.10.1
/v1.0
grammar parse trees into Elasticsearch queries.
Uses elasticsearch_dsl and will produce an elasticsearch_dsl.Q
instance.
optimade/filtertransformers/elasticsearch.py
class ElasticTransformer(BaseTransformer):\n \"\"\"Transformer that transforms ``v0.10.1``/`v1.0` grammar parse\n trees into Elasticsearch queries.\n\n Uses elasticsearch_dsl and will produce an `elasticsearch_dsl.Q` instance.\n\n \"\"\"\n\n operator_map = {\n \"<\": \"lt\",\n \"<=\": \"lte\",\n \">\": \"gt\",\n \">=\": \"gte\",\n }\n\n _quantity_type: type[ElasticsearchQuantity] = ElasticsearchQuantity\n\n def __init__(\n self,\n mapper: type[BaseResourceMapper],\n quantities: Optional[dict[str, Quantity]] = None,\n ):\n if quantities is not None:\n self.quantities = quantities\n\n super().__init__(mapper=mapper)\n\n def _field(\n self, quantity: Union[str, Quantity], nested: Optional[Quantity] = None\n ) -> str:\n \"\"\"Used to unwrap from `property` to the string backend field name.\n\n If passed a `Quantity` (or a derived `ElasticsearchQuantity`), this method\n returns the backend field name, modulo some handling of nested fields.\n\n If passed a string quantity name:\n - Check that the name does not match a relationship type,\n raising a `NotImplementedError` if it does.\n - If the string is prefixed by an underscore, assume this is a\n provider-specific field from another provider and simply return it.\n The original `property` rule would have already filtered out provider\n fields for this backend appropriately as `Quantity` objects.\n\n Returns:\n The field name to use for database queries.\n\n \"\"\"\n\n if isinstance(quantity, str):\n if quantity in self.mapper.RELATIONSHIP_ENTRY_TYPES: # type: ignore[union-attr]\n raise NotImplementedError(\n f\"Unable to filter on relationships with type {quantity!r}\"\n )\n\n # In this case, the property rule has already filtered out fields\n # that do not match this provider, so this indicates an \"other provider\"\n # field that should be passed over\n if quantity.startswith(\"_\"):\n return quantity\n\n if nested is not None:\n return f\"{nested.backend_field}.{quantity.name}\" # type: ignore[union-attr]\n\n return quantity.backend_field # type: ignore[union-attr, return-value]\n\n def _query_op(\n self,\n quantity: Union[ElasticsearchQuantity, str],\n op: str,\n value: Union[str, float, int],\n nested: Optional[ElasticsearchQuantity] = None,\n ) -> Q:\n \"\"\"Return a range, match, or term query for the given quantity, comparison\n operator, and value.\n\n Returns:\n An elasticsearch_dsl query.\n\n Raises:\n BadRequest: If the query is not well-defined or is not supported.\n \"\"\"\n field = self._field(quantity, nested=nested)\n if op in self.operator_map:\n return Q(\"range\", **{field: {self.operator_map[op]: value}})\n\n # If quantity is an \"other provider\" field then use Keyword as the default\n # mapping type. These queries should not match on anything as the field\n # is not present in the index.\n elastic_mapping_type = Keyword\n if isinstance(quantity, ElasticsearchQuantity):\n elastic_mapping_type = quantity.elastic_mapping_type\n\n if elastic_mapping_type == Text:\n query_type = \"match\"\n elif elastic_mapping_type in [Keyword, Integer]:\n query_type = \"term\"\n else:\n raise NotImplementedError(\"Quantity has unsupported ES field type\")\n\n if op in [\"=\", \"\"]:\n return Q(query_type, **{field: value})\n\n if op == \"!=\":\n # != queries must also include an existence check\n # Note that for MongoDB, `$exists` will include null-valued fields,\n # where as in ES `exists` excludes them.\n\n return ~Q(query_type, **{field: value}) & Q(\"exists\", field=field)\n\n def _has_query_op(self, quantities, op, predicate_zip_list):\n \"\"\"Returns a bool query that combines the operator calls `_query_op`\n for each predicate and zipped quantity predicate combination.\n \"\"\"\n if op == \"HAS\":\n kind = \"must\" # in case of HAS we do a must over the \"list\" of the one given element\n elif op == \"HAS ALL\":\n kind = \"must\"\n elif op == \"HAS ANY\":\n kind = \"should\"\n elif op == \"HAS ONLY\":\n # HAS ONLY comes with heavy limitations, because there is no such thing\n # in elastic search. Only supported for elements, where we can construct\n # an anonymous \"formula\" based on elements sorted by order number and\n # can do a = comparision to check if all elements are contained\n\n # @ml-evs: Disabling this HAS ONLY workaround as tests are not passing\n raise NotImplementedError(\n \"HAS ONLY queries are not currently supported by the Elasticsearch backend.\"\n )\n\n # from optimade.models import CHEMICAL_SYMBOLS, ATOMIC_NUMBERS\n\n # if len(quantities) > 1:\n # raise NotImplementedError(\"HAS ONLY is not supported with zip\")\n # quantity = quantities[0]\n\n # if quantity.has_only_quantity is None:\n # raise NotImplementedError(\n # \"HAS ONLY is not supported by %s\" % quantity.name\n # )\n\n # def values():\n # for predicates in predicate_zip_list:\n # if len(predicates) != 1:\n # raise NotImplementedError(\"Tuples not supported in HAS ONLY\")\n # op, value = predicates[0]\n # if op != \"=\":\n # raise NotImplementedError(\n # \"Predicated not supported in HAS ONLY\"\n # )\n # if not isinstance(value, str):\n # raise NotImplementedError(\"Only strings supported in HAS ONLY\")\n # yield value\n\n # try:\n # order_numbers = list([ATOMIC_NUMBERS[element] for element in values()])\n # order_numbers.sort()\n # value = \"\".join(\n # [CHEMICAL_SYMBOLS[number - 1] for number in order_numbers]\n # )\n # except KeyError:\n # raise NotImplementedError(\n # \"HAS ONLY is only supported for chemical symbols\"\n # )\n\n # return Q(\"term\", **{quantity.has_only_quantity.name: value})\n else:\n raise NotImplementedError(f\"Unrecognised operation {op}.\")\n\n queries = [\n self._has_query(quantities, predicates) for predicates in predicate_zip_list\n ]\n return Q(\"bool\", **{kind: queries})\n\n def _has_query(self, quantities, predicates):\n \"\"\"\n Returns a bool query that combines the operator queries ():func:`_query_op`)\n for quantity pericate combination.\n \"\"\"\n if len(quantities) != len(predicates):\n raise ValueError(\n \"Tuple length does not match: %s <o> %s \"\n % (\":\".join(quantities), \":\".join(predicates))\n )\n\n if len(quantities) == 1:\n o, value = predicates[0]\n return self._query_op(quantities[0], o, value)\n\n nested_quantity = quantities[0].nested_quantity\n same_nested_quantity = any(\n q.nested_quantity != nested_quantity for q in quantities\n )\n if nested_quantity is None or same_nested_quantity:\n raise NotImplementedError(\n \"Expression with tuples are only supported for %s\"\n % \", \".join(quantities)\n )\n\n queries = [\n self._query_op(quantity, o, value, nested=nested_quantity)\n for quantity, (o, value) in zip(quantities, predicates)\n ]\n\n return Q(\n \"nested\",\n path=self._field(nested_quantity),\n query=dict(bool=dict(must=queries)),\n )\n\n def __default__(self, data: Any, children: Any, meta: Any) -> Any:\n \"\"\"Default behavior for rules that only replace one symbol with another\"\"\"\n return children[0]\n\n def filter(self, args):\n # filter: expression*\n if len(args) == 1:\n return args[0]\n return Q(\"bool\", **{\"must\": args})\n\n def expression_clause(self, args):\n # expression_clause: expression_phrase ( _AND expression_phrase )*\n result = args[0]\n for arg in args[1:]:\n result &= arg\n return result\n\n def expression(self, args):\n # expression: expression_clause ( _OR expression_clause )*\n result = args[0]\n for arg in args[1:]:\n result |= arg\n return result\n\n def expression_phrase(self, args):\n # expression_phrase: [ NOT ] ( operator | \"(\" expression \")\" )\n if args[0] == \"NOT\":\n return ~args[1]\n return args[0]\n\n @v_args(inline=True)\n def property_first_comparison(self, quantity, query):\n # property_first_comparison: property *_rhs\n return query(quantity)\n\n @v_args(inline=True)\n def constant_first_comparison(self, value, op, quantity):\n # constant_first_comparison: constant OPERATOR ( non_string_value | ...not_implemented_string )\n if not isinstance(quantity, ElasticsearchQuantity):\n raise TypeError(\"Only quantities can be compared to constant values.\")\n\n return self._query_op(quantity, self._reversed_operator_map[op], value)\n\n @v_args(inline=True)\n def value_op_rhs(self, op, value):\n # value_op_rhs: OPERATOR value\n return lambda quantity: self._query_op(quantity, op, value)\n\n def length_op_rhs(self, args):\n # length_op_rhs: LENGTH [ OPERATOR ] signed_int\n value = args[-1]\n if len(args) == 3:\n op = args[1]\n else:\n op = \"=\"\n\n def query(quantity):\n # This is only the case if quantity is an \"other\" provider's field,\n # in which case, we should treat it as unknown and try to do a null query\n if isinstance(quantity, str):\n return self._query_op(quantity, op, value)\n\n if quantity.length_quantity is None:\n raise NotImplementedError(\n f\"LENGTH is not supported for {quantity.name!r}\"\n )\n quantity = quantity.length_quantity\n return self._query_op(quantity, op, value)\n\n return query\n\n @v_args(inline=True)\n def known_op_rhs(self, _, value):\n # known_op_rhs: IS ( KNOWN | UNKNOWN )\n\n def query(quantity):\n query = Q(\"exists\", field=self._field(quantity))\n if value == \"KNOWN\":\n return query\n elif value == \"UNKNOWN\":\n return ~query\n raise NotImplementedError\n\n return query\n\n def set_op_rhs(self, args):\n # set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ... )\n values = args[-1]\n if not isinstance(values, list):\n if len(args) == 3:\n op = args[1]\n else:\n op = \"=\"\n values = [(op, values)]\n\n if len(args) == 3:\n op = \"HAS \" + args[1]\n else:\n op = \"HAS\"\n\n return lambda quantity: self._has_query_op(\n [quantity], op, [[value] for value in values]\n )\n\n def set_zip_op_rhs(self, args):\n # set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list | ANY value_zip_list )\n add_on = args[0]\n values = args[-1]\n if len(args) == 4:\n op = \"HAS \" + args[2]\n else:\n op = \"HAS\"\n values = [values]\n\n return lambda quantity: self._has_query_op([quantity] + add_on, op, values)\n\n def property_zip_addon(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return args\n\n def value_zip(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return self.value_list(args)\n\n def value_zip_list(self, args):\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n return args\n\n def value_list(self, args):\n result = []\n op = \"=\"\n for arg in args:\n if arg in [\"<\", \"<=\", \">\", \">=\", \"!=\", \"=\"]:\n op = arg\n else:\n result.append(\n (\n op,\n arg,\n )\n )\n op = \"=\"\n return result\n\n def fuzzy_string_op_rhs(self, args):\n op = args[0]\n value = args[-1]\n if op == \"CONTAINS\":\n wildcard = \"*%s*\" % value\n if op == \"STARTS\":\n wildcard = \"%s*\" % value\n if op == \"ENDS\":\n wildcard = \"*%s\" % value\n\n return lambda quantity: Q(\"wildcard\", **{self._field(quantity): wildcard})\n\n @v_args(inline=True)\n def string(self, string):\n # string: ESCAPED_STRING\n return string.strip('\"')\n\n @v_args(inline=True)\n def signed_int(self, number):\n # signed_int : SIGNED_INT\n return int(number)\n\n @v_args(inline=True)\n def number(self, number):\n # number: SIGNED_INT | SIGNED_FLOAT\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
__default__(data, children, meta)
","text":"Default behavior for rules that only replace one symbol with another
Source code inoptimade/filtertransformers/elasticsearch.py
def __default__(self, data: Any, children: Any, meta: Any) -> Any:\n \"\"\"Default behavior for rules that only replace one symbol with another\"\"\"\n return children[0]\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.postprocess","title":"postprocess(query)
","text":"Post-process the query according to the rules defined for the backend, returning the backend-specific query.
Source code inoptimade/filtertransformers/base_transformer.py
def postprocess(self, query) -> Any:\n \"\"\"Post-process the query according to the rules defined for\n the backend, returning the backend-specific query.\n\n \"\"\"\n return query\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.property","title":"property(args)
","text":"property: IDENTIFIER ( \".\" IDENTIFIER )*
If this transformer has an associated mapper, the property will be compared to possible relationship entry types and for any supported provider prefixes. If there is a match, this rule will return a string and not a dereferenced Quantity
.
Raises:
Type DescriptionBadRequest
If the property does not match any of the above rules.
Source code inoptimade/filtertransformers/base_transformer.py
def property(self, args: list) -> Any:\n \"\"\"property: IDENTIFIER ( \".\" IDENTIFIER )*\n\n If this transformer has an associated mapper, the property\n will be compared to possible relationship entry types and\n for any supported provider prefixes. If there is a match,\n this rule will return a string and not a dereferenced\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity].\n\n Raises:\n BadRequest: If the property does not match any\n of the above rules.\n\n \"\"\"\n quantity_name = str(args[0])\n\n # If the quantity name matches an entry type (indicating a relationship filter)\n # then simply return the quantity name; the inherited property\n # must then handle any further nested identifiers\n if self.mapper:\n if quantity_name in self.mapper.RELATIONSHIP_ENTRY_TYPES:\n return quantity_name\n\n if self.quantities and quantity_name not in self.quantities:\n # If the quantity is provider-specific, but does not match this provider,\n # then return the quantity name such that it can be treated as unknown.\n # If the prefix does not match another known provider, also emit a warning\n # If the prefix does match a known provider, do not return a warning.\n # Following [Handling unknown property names](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#handling-unknown-property-names)\n if self.mapper and quantity_name.startswith(\"_\"):\n prefix = quantity_name.split(\"_\")[1]\n if prefix not in self.mapper.SUPPORTED_PREFIXES:\n if prefix not in self.mapper.KNOWN_PROVIDER_PREFIXES:\n warnings.warn(\n UnknownProviderProperty(\n f\"Field {quantity_name!r} has an unrecognised prefix: this property has been treated as UNKNOWN.\"\n )\n )\n\n return quantity_name\n\n raise BadRequest(\n detail=f\"'{quantity_name}' is not a known or searchable quantity\"\n )\n\n quantity = self.quantities.get(quantity_name, None)\n if quantity is None:\n quantity = self._quantity_type(name=str(quantity_name))\n\n return quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticsearchQuantity","title":"ElasticsearchQuantity
","text":" Bases: Quantity
Elasticsearch-specific extension of the underlying Quantity
class.
Attributes:
Name Type Descriptionname
str
The name of the quantity as used in the filter expressions.
backend_field
Optional[str]
The name of the field for this quantity in Elasticsearch, will be name
by default.
elastic_mapping_type
Optional[Field]
A decendent of an elasticsearch_dsl.Field
that denotes which mapping type was used in the Elasticsearch index.
length_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.
has_only_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS
) and quantities with this attribute.
nested_quantity
Optional[ElasticsearchQuantity]
To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.
Source code inoptimade/filtertransformers/elasticsearch.py
class ElasticsearchQuantity(Quantity):\n \"\"\"Elasticsearch-specific extension of the underlying\n [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] class.\n\n Attributes:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n name: str\n backend_field: Optional[str]\n length_quantity: Optional[\"ElasticsearchQuantity\"]\n elastic_mapping_type: Optional[Field]\n has_only_quantity: Optional[\"ElasticsearchQuantity\"]\n nested_quantity: Optional[\"ElasticsearchQuantity\"]\n\n def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n elastic_mapping_type: Optional[Field] = None,\n has_only_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n nested_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n ):\n \"\"\"Initialise the quantity from its name, aliases and mapping type.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n super().__init__(name, backend_field, length_quantity)\n\n self.elastic_mapping_type = (\n Keyword if elastic_mapping_type is None else elastic_mapping_type\n )\n self.has_only_quantity = has_only_quantity\n self.nested_quantity = nested_quantity\n
"},{"location":"api_reference/filtertransformers/elasticsearch/#optimade.filtertransformers.elasticsearch.ElasticsearchQuantity.__init__","title":"__init__(name, backend_field=None, length_quantity=None, elastic_mapping_type=None, has_only_quantity=None, nested_quantity=None)
","text":"Initialise the quantity from its name, aliases and mapping type.
Parameters:
Name Type Description Defaultname
str
The name of the quantity as used in the filter expressions.
requiredbackend_field
Optional[str]
The name of the field for this quantity in Elasticsearch, will be name
by default.
None
elastic_mapping_type
Optional[Field]
A decendent of an elasticsearch_dsl.Field
that denotes which mapping type was used in the Elasticsearch index.
None
length_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support length of arrays, but we can map fields with array to other fields with ints about the array length. The LENGTH operator will only be supported for quantities with this attribute.
None
has_only_quantity
Optional[ElasticsearchQuantity]
Elasticsearch does not support exclusive search on arrays, like a list of chemical elements. But, we can order all elements by atomic number and use a keyword field with all elements to perform this search. This only works for elements (i.e. labels in CHEMICAL_SYMBOLS
) and quantities with this attribute.
None
nested_quantity
Optional[ElasticsearchQuantity]
To support optimade's 'zipped tuple' feature (e.g. 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects and nested queries. This quantity will provide the field for the nested object that contains the quantity (and others). The zipped tuples will only work for quantities that share the same nested object quantity.
None
Source code in optimade/filtertransformers/elasticsearch.py
def __init__(\n self,\n name: str,\n backend_field: Optional[str] = None,\n length_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n elastic_mapping_type: Optional[Field] = None,\n has_only_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n nested_quantity: Optional[\"ElasticsearchQuantity\"] = None,\n):\n \"\"\"Initialise the quantity from its name, aliases and mapping type.\n\n Parameters:\n name: The name of the quantity as used in the filter expressions.\n backend_field: The name of the field for this quantity in Elasticsearch, will be\n ``name`` by default.\n elastic_mapping_type: A decendent of an `elasticsearch_dsl.Field` that denotes which\n mapping type was used in the Elasticsearch index.\n length_quantity: Elasticsearch does not support length of arrays, but we can\n map fields with array to other fields with ints about the array length. The\n LENGTH operator will only be supported for quantities with this attribute.\n has_only_quantity: Elasticsearch does not support exclusive search on arrays, like\n a list of chemical elements. But, we can order all elements by atomic number\n and use a keyword field with all elements to perform this search. This only\n works for elements (i.e. labels in ``CHEMICAL_SYMBOLS``) and quantities\n with this attribute.\n nested_quantity: To support optimade's 'zipped tuple' feature (e.g.\n 'elements:elements_ratios HAS \"H\":>0.33), we use elasticsearch nested objects\n and nested queries. This quantity will provide the field for the nested\n object that contains the quantity (and others). The zipped tuples will only\n work for quantities that share the same nested object quantity.\n \"\"\"\n\n super().__init__(name, backend_field, length_quantity)\n\n self.elastic_mapping_type = (\n Keyword if elastic_mapping_type is None else elastic_mapping_type\n )\n self.has_only_quantity = has_only_quantity\n self.nested_quantity = nested_quantity\n
"},{"location":"api_reference/filtertransformers/mongo/","title":"mongo","text":"This submodule implements the MongoTransformer
, which takes the parsed filter and converts it to a valid pymongo/BSON query.
MongoTransformer
","text":" Bases: BaseTransformer
A filter transformer for the MongoDB backend.
Parses a lark tree into a dictionary representation to be used by pymongo or mongomock. Uses post-processing functions to handle some specific edge-cases for MongoDB.
Attributes:
Name Type Descriptionoperator_map
A map from comparison operators to the mongoDB specific versions.
inverse_operator_map
A map from operators to their logical inverse.
mapper
A resource mapper object that defines the expected fields and acts as a container for various field-related configuration.
Source code inoptimade/filtertransformers/mongo.py
class MongoTransformer(BaseTransformer):\n \"\"\"A filter transformer for the MongoDB backend.\n\n Parses a lark tree into a dictionary representation to be\n used by pymongo or mongomock. Uses post-processing functions\n to handle some specific edge-cases for MongoDB.\n\n Attributes:\n operator_map: A map from comparison operators\n to the mongoDB specific versions.\n inverse_operator_map: A map from operators to their\n logical inverse.\n mapper: A resource mapper object that defines the\n expected fields and acts as a container for\n various field-related configuration.\n\n \"\"\"\n\n operator_map = {\n \"<\": \"$lt\",\n \"<=\": \"$lte\",\n \">\": \"$gt\",\n \">=\": \"$gte\",\n \"!=\": \"$ne\",\n \"=\": \"$eq\",\n }\n\n inverse_operator_map = {\n \"$lt\": \"$gte\",\n \"$lte\": \"$gt\",\n \"$gt\": \"$lte\",\n \"$gte\": \"$lt\",\n \"$ne\": \"$eq\",\n \"$eq\": \"$ne\",\n \"$in\": \"$nin\",\n \"$nin\": \"$in\",\n }\n\n def postprocess(self, query: dict[str, Any]):\n \"\"\"Used to post-process the nested dictionary of the parsed query.\"\"\"\n query = self._apply_relationship_filtering(query)\n query = self._apply_length_operators(query)\n query = self._apply_unknown_or_null_filter(query)\n query = self._apply_has_only_filter(query)\n query = self._apply_mongo_id_filter(query)\n query = self._apply_mongo_date_filter(query)\n return query\n\n def value_list(self, arg):\n # value_list: [ OPERATOR ] value ( \",\" [ OPERATOR ] value )*\n # NOTE: no support for optional OPERATOR, yet, so this takes the\n # parsed values and returns an error if that is being attempted\n for value in arg:\n if str(value) in self.operator_map.keys():\n raise NotImplementedError(\n f\"OPERATOR {value} inside value_list {arg} not implemented.\"\n )\n\n return arg\n\n def value_zip(self, arg):\n # value_zip: [ OPERATOR ] value \":\" [ OPERATOR ] value (\":\" [ OPERATOR ] value)*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def value_zip_list(self, arg):\n # value_zip_list: value_zip ( \",\" value_zip )*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def expression(self, arg):\n # expression: expression_clause ( OR expression_clause )\n # expression with and without 'OR'\n return {\"$or\": arg} if len(arg) > 1 else arg[0]\n\n def expression_clause(self, arg):\n # expression_clause: expression_phrase ( AND expression_phrase )*\n # expression_clause with and without 'AND'\n return {\"$and\": arg} if len(arg) > 1 else arg[0]\n\n def expression_phrase(self, arg):\n # expression_phrase: [ NOT ] ( comparison | \"(\" expression \")\" )\n return self._recursive_expression_phrase(arg)\n\n @v_args(inline=True)\n def property_first_comparison(self, quantity, query):\n # property_first_comparison: property ( value_op_rhs | known_op_rhs | fuzzy_string_op_rhs | set_op_rhs |\n # set_zip_op_rhs | length_op_rhs )\n\n # Awkwardly, MongoDB will match null fields in $ne filters,\n # so we need to add a check for null equality in evey $ne query.\n if \"$ne\" in query:\n return {\"$and\": [{quantity: query}, {quantity: {\"$ne\": None}}]}\n\n # Check if a $size query is being made (indicating a length_op_rhs filter); if so, check for\n # a defined length alias to replace the $size call with the corresponding filter on the\n # length quantity then carefully merge the two queries.\n #\n # e.g. `(\"elements\", {\"$size\": 2, \"$all\": [\"Ag\", \"Au\"]})` should become\n # `{\"elements\": {\"$all\": [\"Ag\", \"Au\"]}, \"nelements\": 2}` if the `elements` -> `nelements`\n # length alias is defined.\n if \"$size\" in query:\n if (\n getattr(self.backend_mapping.get(quantity), \"length_quantity\", None)\n is not None\n ):\n size_query = {\n self.backend_mapping[ # type: ignore[union-attr]\n quantity\n ].length_quantity.backend_field: query.pop(\"$size\")\n }\n\n final_query = {}\n if query:\n final_query = {quantity: query}\n for q in size_query:\n if q in final_query:\n final_query[q].update(size_query[q])\n else:\n final_query[q] = size_query[q]\n\n return final_query\n\n return {quantity: query}\n\n def constant_first_comparison(self, arg):\n # constant_first_comparison: constant OPERATOR ( non_string_value | not_implemented_string )\n return self.property_first_comparison(\n arg[2], {self.operator_map[self._reversed_operator_map[arg[1]]]: arg[0]}\n )\n\n @v_args(inline=True)\n def value_op_rhs(self, operator, value):\n # value_op_rhs: OPERATOR value\n return {self.operator_map[operator]: value}\n\n def known_op_rhs(self, arg):\n # known_op_rhs: IS ( KNOWN | UNKNOWN )\n # The OPTIMADE spec also required a type comparison with null, this must be post-processed\n # so here we use a special key \"#known\" which will get replaced in post-processing with the\n # expanded dict\n return {\"#known\": arg[1] == \"KNOWN\"}\n\n def fuzzy_string_op_rhs(self, arg):\n # fuzzy_string_op_rhs: CONTAINS value | STARTS [ WITH ] value | ENDS [ WITH ] value\n\n # The WITH keyword may be omitted.\n if isinstance(arg[1], Token) and arg[1].type == \"WITH\":\n pattern = arg[2]\n else:\n pattern = arg[1]\n\n # CONTAINS\n if arg[0] == \"CONTAINS\":\n regex = f\"{pattern}\"\n elif arg[0] == \"STARTS\":\n regex = f\"^{pattern}\"\n elif arg[0] == \"ENDS\":\n regex = f\"{pattern}$\"\n return {\"$regex\": regex}\n\n def set_op_rhs(self, arg):\n # set_op_rhs: HAS ( [ OPERATOR ] value | ALL value_list | ANY value_list | ONLY value_list )\n\n if len(arg) == 2:\n # only value without OPERATOR\n return {\"$in\": arg[1:]}\n\n if arg[1] == \"ALL\":\n return {\"$all\": arg[2]}\n\n if arg[1] == \"ANY\":\n return {\"$in\": arg[2]}\n\n if arg[1] == \"ONLY\":\n return {\"#only\": arg[2]}\n\n # value with OPERATOR\n raise NotImplementedError(\n f\"set_op_rhs not implemented for use with OPERATOR. Given: {arg}\"\n )\n\n def property(self, args):\n # property: IDENTIFIER ( \".\" IDENTIFIER )*\n quantity = super().property(args)\n if isinstance(quantity, Quantity):\n quantity = quantity.backend_field\n\n return \".\".join([quantity] + args[1:])\n\n def length_op_rhs(self, arg):\n # length_op_rhs: LENGTH [ OPERATOR ] value\n if len(arg) == 2 or (len(arg) == 3 and arg[1] == \"=\"):\n return {\"$size\": arg[-1]}\n\n if arg[1] in self.operator_map and arg[1] != \"!=\":\n # create an invalid query that needs to be post-processed\n # e.g. {'$size': {'$gt': 2}}, which is not allowed by Mongo.\n return {\"$size\": {self.operator_map[arg[1]]: arg[-1]}}\n\n raise NotImplementedError(\n f\"Operator {arg[1]} not implemented for LENGTH filter.\"\n )\n\n def set_zip_op_rhs(self, arg):\n # set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list |\n # ANY value_zip_list )\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def property_zip_addon(self, arg):\n # property_zip_addon: \":\" property (\":\" property)*\n raise NotImplementedError(\"Correlated list queries are not supported.\")\n\n def _recursive_expression_phrase(self, arg: list) -> dict[str, Any]:\n \"\"\"Helper function for parsing `expression_phrase`. Recursively sorts out\n the correct precedence for `$not`, `$and` and `$or`.\n\n Parameters:\n arg: A list containing the expression to be evaluated and whether it\n is negated, e.g., `[\"NOT\", expr]` or just `[expr]`.\n\n Returns:\n The evaluated filter as a nested dictionary.\n\n \"\"\"\n\n def handle_not_and(arg: dict[str, list]) -> dict[str, list]:\n \"\"\"Handle the case of `~(A & B) -> (~A | ~B)`.\n\n We have to check for the special case in which the \"and\" was created\n by a previous NOT, e.g.,\n `NOT (NOT ({\"a\": {\"$eq\": 6}})) -> NOT({\"$and\": [{\"a\": {\"$ne\": 6}},{\"a\": {\"$ne\": None}}]})`\n\n Parameters:\n arg: A dictionary with key `\"$and\"` containing a list of expressions.\n\n Returns:\n A dictionary with key `\"$or\"` containing a list of the appropriate negated expressions.\n \"\"\"\n\n expr1 = arg[\"$and\"][0]\n expr2 = arg[\"$and\"][1]\n if expr1.keys() == expr2.keys():\n key = list(expr1.keys())[0]\n for e, f in itertools.permutations((expr1, expr2)):\n if e.get(key) == {\"$ne\": None}:\n return self._recursive_expression_phrase([\"NOT\", f])\n\n return {\n \"$or\": [\n self._recursive_expression_phrase([\"NOT\", subdict])\n for subdict in arg[\"$and\"]\n ]\n }\n\n def handle_not_or(arg: dict[str, list]) -> dict[str, list]:\n \"\"\"Handle the case of ~(A | B) -> (~A & ~B).\n\n !!! note\n Although the MongoDB `$nor` could be used here, it is not convenient as it\n will also return documents where the filtered field is missing when testing\n for inequality.\n\n Parameters:\n arg: A dictionary with key `\"$or\"` containing a list of expressions.\n\n Returns:\n A dictionary with key `\"$and\"` that lists the appropriate negated expressions.\n \"\"\"\n\n return {\n \"$and\": [\n self._recursive_expression_phrase([\"NOT\", subdict])\n for subdict in arg[\"$or\"]\n ]\n }\n\n if len(arg) == 1:\n # without NOT\n return arg[0]\n\n if \"$or\" in arg[1]:\n return handle_not_or(arg[1])\n\n if \"$and\" in arg[1]:\n return handle_not_and(arg[1])\n\n prop, expr = next(iter(arg[1].items()))\n operator, value = next(iter(expr.items()))\n if operator == \"$not\": # Case of double negation e.g. NOT(\"$not\":{ ...})\n return {prop: value}\n\n # If the NOT operator occurs at the lowest nesting level,\n # the expression can be simplified by using the opposite operator and removing the not.\n if operator in self.inverse_operator_map:\n filter_ = {prop: {self.inverse_operator_map[operator]: value}}\n if operator in (\"$in\", \"$eq\"):\n filter_ = {\"$and\": [filter_, {prop: {\"$ne\": None}}]} # type: ignore[dict-item]\n return filter_\n\n filter_ = {prop: {\"$not\": expr}}\n if \"#known\" in expr:\n return filter_\n return {\"$and\": [filter_, {prop: {\"$ne\": None}}]}\n\n def _apply_length_operators(self, filter_: dict) -> dict:\n \"\"\"Check for any invalid pymongo queries that involve applying a\n comparison operator to the length of a field, and transform\n them into a test for existence of the relevant entry, e.g.\n \"list LENGTH > 3\" becomes \"does the 4th list entry exist?\".\n\n \"\"\"\n\n def check_for_length_op_filter(_, expr):\n return (\n isinstance(expr, dict)\n and \"$size\" in expr\n and isinstance(expr[\"$size\"], dict)\n )\n\n def apply_length_op(subdict, prop, expr):\n # assumes that the dictionary only has one element by design\n # (we just made it above in the transformer)\n operator, value = list(expr[\"$size\"].items())[0]\n if operator in self.operator_map.values() and operator != \"$ne\":\n # worth being explicit here, I think\n _prop = None\n existence = None\n if operator == \"$gt\":\n _prop = f\"{prop}.{value + 1}\"\n existence = True\n elif operator == \"$gte\":\n _prop = f\"{prop}.{value}\"\n existence = True\n elif operator == \"$lt\":\n _prop = f\"{prop}.{value}\"\n existence = False\n elif operator == \"$lte\":\n _prop = f\"{prop}.{value + 1}\"\n existence = False\n if _prop is not None:\n subdict.pop(prop)\n subdict[_prop] = {\"$exists\": existence}\n\n return subdict\n\n return recursive_postprocessing(\n filter_,\n check_for_length_op_filter,\n apply_length_op,\n )\n\n def _apply_relationship_filtering(self, filter_: dict) -> dict:\n \"\"\"Check query for property names that match the entry\n types, and transform them as relationship filters rather than\n property filters.\n\n \"\"\"\n\n def check_for_entry_type(prop, _):\n return str(prop).count(\".\") == 1 and str(prop).split(\".\")[0] in (\n \"structures\",\n \"references\",\n )\n\n def replace_with_relationship(subdict, prop, expr):\n _prop, _field = str(prop).split(\".\")\n if _field != \"id\":\n raise NotImplementedError(\n f'Cannot filter relationships by field \"{_field}\", only \"id\" is supported.'\n )\n\n subdict[f\"relationships.{_prop}.data.{_field}\"] = expr\n subdict.pop(prop)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_entry_type, replace_with_relationship\n )\n\n def _apply_has_only_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces the magic key `\"#only\"`\n with the proper 'HAS ONLY' query.\n \"\"\"\n\n def check_for_only_filter(_, expr):\n \"\"\"Find cases where the magic key `\"#only\"` is in the query.\"\"\"\n return isinstance(expr, dict) and (\"#only\" in expr)\n\n def replace_only_filter(subdict: dict, prop: str, expr: dict):\n \"\"\"Replace the magic key `\"#only\"` (added by this transformer) with an `$elemMatch`-based query.\n\n The first part of the query selects all the documents that contain any value that does not\n match any target values for the property `prop`.\n Subsequently, this selection is inverted, to get the documents that only have\n the allowed values.\n This inversion also selects documents with edge-case values such as null or empty lists;\n these are removed in the second part of the query that makes sure that only documents\n with lists that have at least one value are selected.\n\n \"\"\"\n\n if \"$and\" not in subdict:\n subdict[\"$and\"] = []\n\n if prop.startswith(\"relationships.\"):\n if prop not in (\n \"relationships.references.data.id\",\n \"relationships.structures.data.id\",\n ):\n raise BadRequest(f\"Unable to query on unrecognised field {prop}.\")\n first_part_prop = \".\".join(prop.split(\".\")[:-1])\n subdict[\"$and\"].append(\n {\n first_part_prop: {\n \"$not\": {\"$elemMatch\": {\"id\": {\"$nin\": expr[\"#only\"]}}}\n }\n }\n )\n subdict[\"$and\"].append({first_part_prop + \".0\": {\"$exists\": True}})\n\n else:\n subdict[\"$and\"].append(\n {prop: {\"$not\": {\"$elemMatch\": {\"$nin\": expr[\"#only\"]}}}}\n )\n subdict[\"$and\"].append({prop + \".0\": {\"$exists\": True}})\n\n subdict.pop(prop)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_only_filter, replace_only_filter\n )\n\n def _apply_unknown_or_null_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces the check for\n KNOWN with a check for existence and a check for not null, and the\n inverse for UNKNOWN.\n\n \"\"\"\n\n def check_for_known_filter(_, expr):\n \"\"\"Find cases where the query dict looks like\n `{\"field\": {\"#known\": T/F}}` or\n `{\"field\": \"$not\": {\"#known\": T/F}}`, which is a magic word\n for KNOWN/UNKNOWN filters in this transformer.\n\n \"\"\"\n return isinstance(expr, dict) and (\n \"#known\" in expr or \"#known\" in expr.get(\"$not\", {})\n )\n\n def replace_known_filter_with_or(subdict, prop, expr):\n \"\"\"Replace magic key `\"#known\"` (added by this transformer) with the appropriate\n combination of `$exists` and/or test for nullity.\n combination of $exists and/or $eq/$ne null.\n\n \"\"\"\n not_ = set(expr.keys()) == {\"$not\"}\n if not_:\n expr = expr[\"$not\"]\n\n exists = expr[\"#known\"] ^ not_\n\n top_level_key = \"$or\"\n comparison_operator = \"$eq\"\n if exists:\n top_level_key = \"$and\"\n comparison_operator = \"$ne\"\n\n if top_level_key not in subdict:\n subdict[top_level_key] = []\n\n subdict[top_level_key].append({prop: {\"$exists\": exists}})\n subdict[top_level_key].append({prop: {comparison_operator: None}})\n\n subdict.pop(prop)\n\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_known_filter, replace_known_filter_with_or\n )\n\n def _apply_mongo_id_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces any operations\n on the special Mongodb `_id` key with the corresponding operation\n on a BSON `ObjectId` type.\n \"\"\"\n\n def check_for_id_key(prop, _):\n \"\"\"Find cases where the query dict is operating on the `_id` field.\"\"\"\n return prop == \"_id\"\n\n def replace_str_id_with_objectid(subdict, prop, expr):\n from bson import ObjectId\n\n for operator in subdict[prop]:\n val = subdict[prop][operator]\n if operator not in (\"$eq\", \"$ne\"):\n if self.mapper is not None:\n prop = self.mapper.get_optimade_field(prop)\n raise NotImplementedError(\n f\"Operator {operator} not supported for query on field {prop!r}, can only test for equality\"\n )\n if isinstance(val, str):\n subdict[prop][operator] = ObjectId(val)\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_id_key, replace_str_id_with_objectid\n )\n\n def _apply_mongo_date_filter(self, filter_: dict) -> dict:\n \"\"\"This method loops through the query and replaces any operations\n on suspected timestamp properties with the corresponding operation\n on a BSON `DateTime` type.\n \"\"\"\n\n def check_for_timestamp_field(prop, _):\n \"\"\"Find cases where the query dict is operating on a timestamp field.\"\"\"\n if self.mapper is not None:\n prop = self.mapper.get_optimade_field(prop)\n return prop == \"last_modified\"\n\n def replace_str_date_with_datetime(subdict, prop, expr):\n \"\"\"Encode suspected dates in with BSON.\"\"\"\n import bson.json_util\n\n for operator in subdict[prop]:\n query_datetime = bson.json_util.loads(\n bson.json_util.dumps({\"$date\": subdict[prop][operator]}),\n json_options=bson.json_util.DEFAULT_JSON_OPTIONS.with_options(\n tz_aware=True, tzinfo=bson.tz_util.utc\n ),\n )\n if query_datetime.microsecond != 0:\n warnings.warn(\n f\"Query for timestamp {subdict[prop][operator]!r} for field {prop!r} contained microseconds, which is not RFC3339 compliant. \"\n \"This may cause undefined behaviour for the underlying database.\",\n TimestampNotRFCCompliant,\n )\n\n subdict[prop][operator] = query_datetime\n\n return subdict\n\n return recursive_postprocessing(\n filter_, check_for_timestamp_field, replace_str_date_with_datetime\n )\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.backend_mapping","title":"backend_mapping: dict[str, Quantity]
property
","text":"A mapping between backend field names (aliases) and the corresponding Quantity
object.
quantities: dict[str, Quantity]
property
writable
","text":"A mapping from the OPTIMADE field name to the corresponding Quantity
objects.
__default__(data, children, meta)
","text":"The default rule to call when no definition is found for a particular construct.
Source code inoptimade/filtertransformers/base_transformer.py
def __default__(self, data, children, meta):\n \"\"\"The default rule to call when no definition is found for a particular construct.\"\"\"\n raise NotImplementedError(\n f\"Calling __default__, i.e., unknown grammar concept. data: {data}, children: {children}, meta: {meta}\"\n )\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.__init__","title":"__init__(mapper=None)
","text":"Initialise the transformer object, optionally loading in a resource mapper for use when post-processing.
Source code inoptimade/filtertransformers/base_transformer.py
def __init__(self, mapper: Optional[type[BaseResourceMapper]] = None):\n \"\"\"Initialise the transformer object, optionally loading in a\n resource mapper for use when post-processing.\n\n \"\"\"\n self.mapper = mapper\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.comparison","title":"comparison(value)
","text":"comparison: constant_first_comparison | property_first_comparison
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef comparison(self, value):\n \"\"\"comparison: constant_first_comparison | property_first_comparison\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.constant","title":"constant(value)
","text":"constant: string | number
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef constant(self, value):\n \"\"\"constant: string | number\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.filter","title":"filter(arg)
","text":"filter: expression*
Source code inoptimade/filtertransformers/base_transformer.py
def filter(self, arg):\n \"\"\"filter: expression*\"\"\"\n return arg[0] if arg else None\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.non_string_value","title":"non_string_value(value)
","text":"non_string_value: number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef non_string_value(self, value):\n \"\"\"non_string_value: number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.not_implemented_string","title":"not_implemented_string(value)
","text":"not_implemented_string: value
Raises:
Type DescriptionNotImplementedError
For further information, see Materials-Consortia/OPTIMADE issue 157: https://github.com/Materials-Consortia/OPTIMADE/issues/157
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef not_implemented_string(self, value):\n \"\"\"not_implemented_string: value\n\n Raises:\n NotImplementedError: For further information, see Materials-Consortia/OPTIMADE issue 157:\n https://github.com/Materials-Consortia/OPTIMADE/issues/157\n\n \"\"\"\n raise NotImplementedError(\"Comparing strings is not yet implemented.\")\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.number","title":"number(number)
","text":"number: SIGNED_INT | SIGNED_FLOAT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef number(self, number):\n \"\"\"number: SIGNED_INT | SIGNED_FLOAT\"\"\"\n if TYPE_CHECKING: # pragma: no cover\n type_: Union[type[int], type[float]]\n\n if number.type == \"SIGNED_INT\":\n type_ = int\n elif number.type == \"SIGNED_FLOAT\":\n type_ = float\n return type_(number)\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.postprocess","title":"postprocess(query)
","text":"Used to post-process the nested dictionary of the parsed query.
Source code inoptimade/filtertransformers/mongo.py
def postprocess(self, query: dict[str, Any]):\n \"\"\"Used to post-process the nested dictionary of the parsed query.\"\"\"\n query = self._apply_relationship_filtering(query)\n query = self._apply_length_operators(query)\n query = self._apply_unknown_or_null_filter(query)\n query = self._apply_has_only_filter(query)\n query = self._apply_mongo_id_filter(query)\n query = self._apply_mongo_date_filter(query)\n return query\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.signed_int","title":"signed_int(number)
","text":"signed_int : SIGNED_INT
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef signed_int(self, number):\n \"\"\"signed_int : SIGNED_INT\"\"\"\n return int(number)\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.string","title":"string(string)
","text":"string: ESCAPED_STRING
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef string(self, string):\n \"\"\"string: ESCAPED_STRING\"\"\"\n return string.strip('\"')\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.transform","title":"transform(tree)
","text":"Transform the query using the Lark Transformer
then run the backend-specific post-processing methods.
optimade/filtertransformers/base_transformer.py
def transform(self, tree: Tree) -> Any:\n \"\"\"Transform the query using the Lark `Transformer` then run the\n backend-specific post-processing methods.\n\n \"\"\"\n return self.postprocess(super().transform(tree))\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.MongoTransformer.value","title":"value(value)
","text":"value: string | number | property
Source code inoptimade/filtertransformers/base_transformer.py
@v_args(inline=True)\ndef value(self, value):\n \"\"\"value: string | number | property\"\"\"\n # Note: Return as is.\n return value\n
"},{"location":"api_reference/filtertransformers/mongo/#optimade.filtertransformers.mongo.recursive_postprocessing","title":"recursive_postprocessing(filter_, condition, replacement)
","text":"Recursively descend into the query, checking each dictionary (contained in a list, or as an entry in another dictionary) for the condition passed. If the condition is true, apply the replacement to the dictionary.
Parameters:
Name Type Description Defaultfilter_
the filter_ to process.
requiredcondition
callable
a function that returns True if the replacement function should be applied. It should take as arguments the property and expression from the filter_, as would be returned by iterating over filter_.items()
.
replacement
callable
a function that returns the processed dictionary. It should take as arguments the dictionary to modify, the property and the expression (as described above).
required ExampleFor the simple case of replacing one field name with another, the following functions could be used:
def condition(prop, expr):\n return prop == \"field_name_old\"\n\ndef replacement(d, prop, expr):\n d[\"field_name_old\"] = d.pop(prop)\n\nfilter_ = recursive_postprocessing(\n filter_, condition, replacement\n)\n
Source code in optimade/filtertransformers/mongo.py
def recursive_postprocessing(filter_: Union[dict, list], condition, replacement):\n \"\"\"Recursively descend into the query, checking each dictionary\n (contained in a list, or as an entry in another dictionary) for\n the condition passed. If the condition is true, apply the\n replacement to the dictionary.\n\n Parameters:\n filter_ : the filter_ to process.\n condition (callable): a function that returns True if the\n replacement function should be applied. It should take\n as arguments the property and expression from the filter_,\n as would be returned by iterating over `filter_.items()`.\n replacement (callable): a function that returns the processed\n dictionary. It should take as arguments the dictionary\n to modify, the property and the expression (as described\n above).\n\n Example:\n For the simple case of replacing one field name with\n another, the following functions could be used:\n\n ```python\n def condition(prop, expr):\n return prop == \"field_name_old\"\n\n def replacement(d, prop, expr):\n d[\"field_name_old\"] = d.pop(prop)\n\n filter_ = recursive_postprocessing(\n filter_, condition, replacement\n )\n\n ```\n\n \"\"\"\n if isinstance(filter_, list):\n result = [recursive_postprocessing(q, condition, replacement) for q in filter_]\n return result\n\n if isinstance(filter_, dict):\n # this could potentially lead to memory leaks if the filter_ is *heavily* nested\n _cached_filter = copy.deepcopy(filter_)\n for prop, expr in filter_.items():\n if condition(prop, expr):\n _cached_filter = replacement(_cached_filter, prop, expr)\n elif isinstance(expr, list):\n _cached_filter[prop] = [\n recursive_postprocessing(q, condition, replacement) for q in expr\n ]\n return _cached_filter\n\n return filter_\n
"},{"location":"api_reference/models/baseinfo/","title":"baseinfo","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.VERSIONED_BASE_URL_PATTERN","title":"VERSIONED_BASE_URL_PATTERN = '^.+/v[0-1](\\\\.[0-9]+)*/?$'
module-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion","title":"AvailableApiVersion
","text":" Bases: BaseModel
A JSON object containing information about an available API version
Source code inoptimade/models/baseinfo.py
class AvailableApiVersion(BaseModel):\n \"\"\"A JSON object containing information about an available API version\"\"\"\n\n url: Annotated[\n AnyHttpUrl,\n StrictField(\n description=\"A string specifying a versioned base URL that MUST adhere to the rules in section Base URL\",\n json_schema_extra={\n \"pattern\": VERSIONED_BASE_URL_PATTERN,\n },\n ),\n ]\n\n version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n @field_validator(\"url\", mode=\"after\")\n @classmethod\n def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.url","title":"url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.version","title":"version: Annotated[SemanticVersion, StrictField(description=\"A string containing the full version number of the API served at that versioned base URL.\\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\\nExamples: `1.0.0`, `1.0.0-rc.2`.\")]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.crosscheck_url_and_version","title":"crosscheck_url_and_version()
","text":"Check that URL version and API version are compatible.
Source code inoptimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef crosscheck_url_and_version(self) -> \"AvailableApiVersion\":\n \"\"\"Check that URL version and API version are compatible.\"\"\"\n url = (\n str(self.url)\n .split(\"/\")[-2 if str(self.url).endswith(\"/\") else -1]\n .replace(\"v\", \"\")\n )\n # as with version urls, we need to split any release tags or build metadata out of these URLs\n url_version = tuple(\n int(val) for val in url.split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n api_version = tuple(\n int(val) for val in str(self.version).split(\"-\")[0].split(\"+\")[0].split(\".\")\n )\n if any(a != b for a, b in zip(url_version, api_version)):\n raise ValueError(\n f\"API version {api_version} is not compatible with url version {url_version}.\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.AvailableApiVersion.url_must_be_versioned_base_Url","title":"url_must_be_versioned_base_Url(value)
classmethod
","text":"The URL must be a versioned base URL
Source code inoptimade/models/baseinfo.py
@field_validator(\"url\", mode=\"after\")\n@classmethod\ndef url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:\n \"\"\"The URL must be a versioned base URL\"\"\"\n if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):\n raise ValueError(\n f\"URL {value} must be a versioned base URL (i.e., must match the \"\n f\"pattern '{VERSIONED_BASE_URL_PATTERN}')\"\n )\n return value\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes","title":"BaseInfoAttributes
","text":" Bases: BaseModel
Attributes for Base URL Info endpoint
Source code inoptimade/models/baseinfo.py
class BaseInfoAttributes(BaseModel):\n \"\"\"Attributes for Base URL Info endpoint\"\"\"\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n available_api_versions: Annotated[\n list[AvailableApiVersion],\n StrictField(\n description=\"A list of dictionaries of available API versions at other base URLs\",\n ),\n ]\n formats: Annotated[\n list[str], StrictField(description=\"List of available output formats.\")\n ] = [\"json\"]\n available_endpoints: Annotated[\n list[str],\n StrictField(\n description=\"List of available endpoints (i.e., the string to be appended to the versioned base URL).\",\n ),\n ]\n entry_types_by_format: Annotated[\n dict[str, list[str]],\n StrictField(\n description=\"Available entry endpoints as a function of output formats.\"\n ),\n ]\n is_index: Annotated[\n Optional[bool],\n StrictField(\n description=\"If true, this is an index meta-database base URL (see section Index Meta-Database). \"\n \"If this member is not provided, the client MUST assume this is not an index meta-database base URL \"\n \"(i.e., the default is for `is_index` to be `false`).\",\n ),\n ] = False\n\n @model_validator(mode=\"after\")\n def formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.is_index","title":"is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource","title":"BaseInfoResource
","text":" Bases: Resource
optimade/models/baseinfo.py
class BaseInfoResource(Resource):\n id: Literal[\"/\"] = \"/\"\n type: Literal[\"info\"] = \"info\"\n attributes: BaseInfoAttributes\n
"},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.attributes","title":"attributes: BaseInfoAttributes
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/baseinfo/#optimade.models.baseinfo.BaseInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/","title":"entries","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty","title":"EntryInfoProperty
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoProperty(BaseModel):\n description: Annotated[\n str,\n StrictField(description=\"A human-readable description of the entry property\"),\n ]\n\n unit: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.\"\"\",\n ),\n ] = None\n\n sortable: Annotated[\n Optional[bool],\n StrictField(\n description=\"\"\"Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.\"\"\",\n ),\n ] = None\n\n type: Annotated[\n Optional[DataType],\n StrictField(\n title=\"Type\",\n description=\"\"\"The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.description","title":"description: Annotated[str, StrictField(description='A human-readable description of the entry property')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.sortable","title":"sortable: Annotated[Optional[bool], StrictField(description='Defines whether the entry property can be used for sorting with the \"sort\" parameter.\\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.type","title":"type: Annotated[Optional[DataType], StrictField(title=Type, description=\"The type of the property's value.\\nThis MUST be any of the types defined in the Data types section.\\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\\nNote, if the value is a nested type, only the outermost type should be reported.\\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoProperty.unit","title":"unit: Annotated[Optional[str], StrictField(description='The physical unit of the entry property.\\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource","title":"EntryInfoResource
","text":" Bases: BaseModel
optimade/models/entries.py
class EntryInfoResource(BaseModel):\n formats: Annotated[\n list[str],\n StrictField(\n description=\"List of output formats available for this type of entry.\"\n ),\n ]\n\n description: Annotated[str, StrictField(description=\"Description of the entry.\")]\n\n properties: Annotated[\n dict[ValidIdentifier, EntryInfoProperty],\n StrictField(\n description=\"A dictionary describing queryable properties for this entry type, where each key is a property name.\",\n ),\n ]\n\n output_fields_by_format: Annotated[\n dict[str, list[ValidIdentifier]],\n StrictField(\n description=\"Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.\",\n ),\n ]\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.description","title":"description: Annotated[str, StrictField(description='Description of the entry.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.formats","title":"formats: Annotated[list[str], StrictField(description='List of output formats available for this type of entry.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.output_fields_by_format","title":"output_fields_by_format: Annotated[dict[str, list[ValidIdentifier]], StrictField(description='Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryInfoResource.properties","title":"properties: Annotated[dict[ValidIdentifier, EntryInfoProperty], StrictField(description='A dictionary describing queryable properties for this entry type, where each key is a property name.')]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships","title":"EntryRelationships
","text":" Bases: Relationships
This model wraps the JSON API Relationships to include type-specific top level keys.
Source code inoptimade/models/entries.py
class EntryRelationships(Relationships):\n \"\"\"This model wraps the JSON API Relationships to include type-specific top level keys.\"\"\"\n\n references: Annotated[\n Optional[ReferenceRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `references` type.\",\n ),\n ] = None\n\n structures: Annotated[\n Optional[StructureRelationship],\n StrictField(\n description=\"Object containing links to relationships with entries of the `structures` type.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.references","title":"references: Annotated[Optional[ReferenceRelationship], StrictField(description='Object containing links to relationships with entries of the `references` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.structures","title":"structures: Annotated[Optional[StructureRelationship], StrictField(description='Object containing links to relationships with entries of the `structures` type.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryRelationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource","title":"EntryResource
","text":" Bases: Resource
The base model for an entry resource.
Source code inoptimade/models/entries.py
class EntryResource(Resource):\n \"\"\"The base model for an entry resource.\"\"\"\n\n id: Annotated[\n str,\n OptimadeField(\n description=\"\"\"An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n type: Annotated[\n str,\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Example**: `\"structures\"`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n attributes: Annotated[\n EntryResourceAttributes,\n StrictField(\n description=\"\"\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\"\"\",\n ),\n ]\n\n relationships: Annotated[\n Optional[EntryRelationships],\n StrictField(\n description=\"\"\"A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.attributes","title":"attributes: Annotated[EntryResourceAttributes, StrictField(description=\"A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).\")]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResource.type","title":"type: Annotated[str, OptimadeField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Example**: `\"structures\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes","title":"EntryResourceAttributes
","text":" Bases: Attributes
Contains key-value pairs representing the entry's properties.
Source code inoptimade/models/entries.py
class EntryResourceAttributes(Attributes):\n \"\"\"Contains key-value pairs representing the entry's properties.\"\"\"\n\n immutable_id: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n last_modified: Annotated[\n Optional[datetime],\n OptimadeField(\n description=\"\"\"Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @field_validator(\"immutable_id\", mode=\"before\")\n @classmethod\n def cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.EntryResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship","title":"ReferenceRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class ReferenceRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"references\"]] = \"references\"\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.ReferenceRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship","title":"StructureRelationship
","text":" Bases: TypedRelationship
optimade/models/entries.py
class StructureRelationship(TypedRelationship):\n _req_type: ClassVar[Literal[\"structures\"]] = \"structures\"\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.StructureRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship","title":"TypedRelationship
","text":" Bases: Relationship
optimade/models/entries.py
class TypedRelationship(Relationship):\n _req_type: ClassVar[str]\n\n @field_validator(\"data\", mode=\"after\")\n @classmethod\n def check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n ) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/entries/#optimade.models.entries.TypedRelationship.check_rel_type","title":"check_rel_type(data)
classmethod
","text":"Source code in optimade/models/entries.py
@field_validator(\"data\", mode=\"after\")\n@classmethod\ndef check_rel_type(\n cls, data: Union[BaseRelationshipResource, list[BaseRelationshipResource]]\n) -> list[BaseRelationshipResource]:\n if not isinstance(data, list):\n # All relationships at this point are empty-to-many relationships in JSON:API:\n # https://jsonapi.org/format/1.0/#document-resource-object-linkage\n raise ValueError(\"`data` key in a relationship must always store a list.\")\n\n if any(obj.type != cls._req_type for obj in data):\n raise ValueError(\"Object stored in relationship data has wrong type\")\n\n return data\n
"},{"location":"api_reference/models/index_metadb/","title":"index_metadb","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes","title":"IndexInfoAttributes
","text":" Bases: BaseInfoAttributes
Attributes for Base URL Info endpoint for an Index Meta-Database
Source code inoptimade/models/index_metadb.py
class IndexInfoAttributes(BaseInfoAttributes):\n \"\"\"Attributes for Base URL Info endpoint for an Index Meta-Database\"\"\"\n\n is_index: Annotated[\n bool,\n StrictField(\n description=\"This must be `true` since this is an index meta-database (see section Index Meta-Database).\",\n ),\n ] = True\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.available_api_versions","title":"available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.available_endpoints","title":"available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.entry_types_by_format","title":"entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.formats","title":"formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json']
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.is_index","title":"is_index: Annotated[bool, StrictField(description='This must be `true` since this is an index meta-database (see section Index Meta-Database).')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoAttributes.formats_and_endpoints_must_be_valid","title":"formats_and_endpoints_must_be_valid()
","text":"Source code in optimade/models/baseinfo.py
@model_validator(mode=\"after\")\ndef formats_and_endpoints_must_be_valid(self) -> \"BaseInfoAttributes\":\n for format_, endpoints in self.entry_types_by_format.items():\n if format_ not in self.formats:\n raise ValueError(f\"'{format_}' must be listed in formats to be valid\")\n for endpoint in endpoints:\n if endpoint not in self.available_endpoints:\n raise ValueError(\n f\"'{endpoint}' must be listed in available_endpoints to be valid\"\n )\n return self\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource","title":"IndexInfoResource
","text":" Bases: BaseInfoResource
Index Meta-Database Base URL Info endpoint resource
Source code inoptimade/models/index_metadb.py
class IndexInfoResource(BaseInfoResource):\n \"\"\"Index Meta-Database Base URL Info endpoint resource\"\"\"\n\n attributes: IndexInfoAttributes\n relationships: Annotated[ # type: ignore[assignment]\n Optional[dict[Literal[\"default\"], IndexRelationship]],\n StrictField(\n title=\"Relationships\",\n description=\"\"\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.attributes","title":"attributes: IndexInfoAttributes
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.id","title":"id: Literal['/'] = '/'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.relationships","title":"relationships: Annotated[Optional[dict[Literal['default'], IndexRelationship]], StrictField(title=Relationships, description=\"Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\\nA client SHOULD present this database as the first choice when an end-user chooses this provider.\")]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexInfoResource.type","title":"type: Literal['info'] = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexRelationship","title":"IndexRelationship
","text":" Bases: BaseModel
Index Meta-Database relationship
Source code inoptimade/models/index_metadb.py
class IndexRelationship(BaseModel):\n \"\"\"Index Meta-Database relationship\"\"\"\n\n data: Annotated[\n Optional[RelatedLinksResource],\n StrictField(\n description=\"\"\"[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.IndexRelationship.data","title":"data: Annotated[Optional[RelatedLinksResource], StrictField(description='[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource","title":"RelatedLinksResource
","text":" Bases: BaseResource
A related Links resource object
Source code inoptimade/models/index_metadb.py
class RelatedLinksResource(BaseResource):\n \"\"\"A related Links resource object\"\"\"\n\n type: Literal[\"links\"] = \"links\"\n
"},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/index_metadb/#optimade.models.index_metadb.RelatedLinksResource.type","title":"type: Literal['links'] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/","title":"jsonapi","text":"This module should reproduce JSON API v1.0 https://jsonapi.org/format/1.0/
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonLinkType","title":"JsonLinkType = Union[AnyUrl, Link]
module-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes","title":"Attributes
","text":" Bases: BaseModel
Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined. The keys for Attributes MUST NOT be: relationships links id type
Source code inoptimade/models/jsonapi.py
class Attributes(BaseModel):\n \"\"\"\n Members of the attributes object (\"attributes\\\") represent information about the resource object in which it's defined.\n The keys for Attributes MUST NOT be:\n relationships\n links\n id\n type\n \"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n @model_validator(mode=\"after\")\n def check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Attributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource","title":"BaseResource
","text":" Bases: BaseModel
Minimum requirements to represent a Resource
Source code inoptimade/models/jsonapi.py
class BaseResource(BaseModel):\n \"\"\"Minimum requirements to represent a Resource\"\"\"\n\n model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)\n\n id: Annotated[str, StrictField(description=\"Resource ID\")]\n type: Annotated[str, StrictField(description=\"Resource type\")]\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.BaseResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error","title":"Error
","text":" Bases: BaseModel
An error response
Source code inoptimade/models/jsonapi.py
class Error(BaseModel):\n \"\"\"An error response\"\"\"\n\n id: Annotated[\n Optional[str],\n StrictField(\n description=\"A unique identifier for this particular occurrence of the problem.\",\n ),\n ] = None\n links: Annotated[\n Optional[ErrorLinks], StrictField(description=\"A links object storing about\")\n ] = None\n status: Annotated[\n Optional[Annotated[str, BeforeValidator(str)]],\n StrictField(\n description=\"the HTTP status code applicable to this problem, expressed as a string value.\",\n ),\n ] = None\n code: Annotated[\n Optional[str],\n StrictField(\n description=\"an application-specific error code, expressed as a string value.\",\n ),\n ] = None\n title: Annotated[\n Optional[str],\n StrictField(\n description=\"A short, human-readable summary of the problem. \"\n \"It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.\",\n ),\n ] = None\n detail: Annotated[\n Optional[str],\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ] = None\n source: Annotated[\n Optional[ErrorSource],\n StrictField(\n description=\"An object containing references to the source of the error\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the error.\",\n ),\n ] = None\n\n def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.detail","title":"detail: Annotated[Optional[str], StrictField(description='A human-readable explanation specific to this occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Error.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorLinks","title":"ErrorLinks
","text":" Bases: BaseModel
A Links object specific to Error objects
Source code inoptimade/models/jsonapi.py
class ErrorLinks(BaseModel):\n \"\"\"A Links object specific to Error objects\"\"\"\n\n about: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that leads to further details about this particular occurrence of the problem.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorLinks.about","title":"about: Annotated[Optional[JsonLinkType], StrictField(description='A link that leads to further details about this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource","title":"ErrorSource
","text":" Bases: BaseModel
an object containing references to the source of the error
Source code inoptimade/models/jsonapi.py
class ErrorSource(BaseModel):\n \"\"\"an object containing references to the source of the error\"\"\"\n\n pointer: Annotated[\n Optional[str],\n StrictField(\n description=\"a JSON Pointer [RFC6901] to the associated entity in the request document \"\n '[e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].',\n ),\n ] = None\n parameter: Annotated[\n Optional[str],\n StrictField(\n description=\"a string indicating which URI query parameter caused the error.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource.parameter","title":"parameter: Annotated[Optional[str], StrictField(description='a string indicating which URI query parameter caused the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ErrorSource.pointer","title":"pointer: Annotated[Optional[str], StrictField(description='a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi","title":"JsonApi
","text":" Bases: BaseModel
An object describing the server's implementation
Source code inoptimade/models/jsonapi.py
class JsonApi(BaseModel):\n \"\"\"An object describing the server's implementation\"\"\"\n\n version: Annotated[str, StrictField(description=\"Version of the json API used\")] = (\n \"1.0\"\n )\n meta: Annotated[\n Optional[Meta], StrictField(description=\"Non-standard meta information\")\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='Non-standard meta information')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.JsonApi.version","title":"version: Annotated[str, StrictField(description='Version of the json API used')] = '1.0'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link","title":"Link
","text":" Bases: BaseModel
A link MUST be represented as either: a string containing the link's URL or a link object.
Source code inoptimade/models/jsonapi.py
class Link(BaseModel):\n \"\"\"A link **MUST** be represented as either: a string containing the link's URL or a link object.\"\"\"\n\n href: Annotated[\n AnyUrl, StrictField(description=\"a string containing the link's URL.\")\n ]\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about the link.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link.href","title":"href: Annotated[AnyUrl, StrictField(description=\"a string containing the link's URL.\")]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Link.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the link.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Meta","title":"Meta
","text":" Bases: BaseModel
Non-standard meta-information that can not be represented as an attribute or relationship.
Source code inoptimade/models/jsonapi.py
class Meta(BaseModel):\n \"\"\"Non-standard meta-information that can not be represented as an attribute or relationship.\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Meta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship","title":"Relationship
","text":" Bases: BaseModel
Representation references from the resource object in which it's defined to other resource objects.
Source code inoptimade/models/jsonapi.py
class Relationship(BaseModel):\n \"\"\"Representation references from the resource object in which it's defined to other resource objects.\"\"\"\n\n links: Annotated[\n Optional[RelationshipLinks],\n StrictField(\n description=\"a links object containing at least one of the following: self, related\",\n ),\n ] = None\n data: Annotated[\n Optional[Union[BaseResource, list[BaseResource]]],\n StrictField(description=\"Resource linkage\"),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object that contains non-standard meta-information about the relationship.\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.data","title":"data: Annotated[Optional[Union[BaseResource, list[BaseResource]]], StrictField(description='Resource linkage')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks","title":"RelationshipLinks
","text":" Bases: BaseModel
A resource object MAY contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.
Source code inoptimade/models/jsonapi.py
class RelationshipLinks(BaseModel):\n \"\"\"A resource object **MAY** contain references to other resource objects (\"relationships\").\n Relationships may be to-one or to-many.\n Relationships can be specified by including a member in a resource's links object.\n\n \"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"\"\"A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\"\"\",\n ),\n ] = None\n related: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).\",\n ),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links).')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description=\"A link for the relationship itself (a 'relationship link').\\nThis link allows the client to directly manipulate the relationship.\\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.RelationshipLinks.either_self_or_related_must_be_specified","title":"either_self_or_related_must_be_specified()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_self_or_related_must_be_specified(self) -> \"RelationshipLinks\":\n if self.self is None and self.related is None:\n raise ValueError(\n \"Either 'self' or 'related' MUST be specified for RelationshipLinks\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationships","title":"Relationships
","text":" Bases: BaseModel
Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects. Keys MUST NOT be: type id
Source code inoptimade/models/jsonapi.py
class Relationships(BaseModel):\n \"\"\"\n Members of the relationships object (\\\"relationships\\\") represent references from the resource object in which it's defined to other resource objects.\n Keys MUST NOT be:\n type\n id\n \"\"\"\n\n @model_validator(mode=\"after\")\n def check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Relationships.check_illegal_relationships_fields","title":"check_illegal_relationships_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_relationships_fields(self) -> \"Relationships\":\n illegal_fields = (\"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Relationships\"\n )\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource","title":"Resource
","text":" Bases: BaseResource
Resource objects appear in a JSON API document to represent resources.
Source code inoptimade/models/jsonapi.py
class Resource(BaseResource):\n \"\"\"Resource objects appear in a JSON API document to represent resources.\"\"\"\n\n links: Annotated[\n Optional[ResourceLinks],\n StrictField(\n description=\"a links object containing links related to the resource.\"\n ),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.\",\n ),\n ] = None\n attributes: Annotated[\n Optional[Attributes],\n StrictField(\n description=\"an attributes object representing some of the resource\u2019s data.\",\n ),\n ] = None\n relationships: Annotated[\n Optional[Relationships],\n StrictField(\n description=\"\"\"[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.attributes","title":"attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource\u2019s data.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.relationships","title":"relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\\ndescribing relationships between the resource and other JSON API resources.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Resource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ResourceLinks","title":"ResourceLinks
","text":" Bases: BaseModel
A Resource Links object
Source code inoptimade/models/jsonapi.py
class ResourceLinks(BaseModel):\n \"\"\"A Resource Links object\"\"\"\n\n self: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"A link that identifies the resource represented by the resource object.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ResourceLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link that identifies the resource represented by the resource object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response","title":"Response
","text":" Bases: BaseModel
A top-level response.
Source code inoptimade/models/jsonapi.py
class Response(BaseModel):\n \"\"\"A top-level response.\"\"\"\n\n data: Annotated[\n Optional[Union[None, Resource, list[Resource]]],\n StrictField(description=\"Outputted Data\", uniqueItems=True),\n ] = None\n meta: Annotated[\n Optional[Meta],\n StrictField(\n description=\"A meta object containing non-standard information related to the Success\",\n ),\n ] = None\n errors: Annotated[\n Optional[list[Error]],\n StrictField(description=\"A list of unique errors\", uniqueItems=True),\n ] = None\n included: Annotated[\n Optional[list[Resource]],\n StrictField(\n description=\"A list of unique included resources\", uniqueItems=True\n ),\n ] = None\n links: Annotated[\n Optional[ToplevelLinks],\n StrictField(description=\"Links associated with the primary data or errors\"),\n ] = None\n jsonapi: Annotated[\n Optional[JsonApi],\n StrictField(description=\"Information about the JSON API used\"),\n ] = None\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n\n model_config = ConfigDict(\n json_encoders={\n datetime: lambda v: v.astimezone(timezone.utc).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n )\n }\n )\n \"\"\"The specification mandates that datetimes must be encoded following\n [RFC3339](https://tools.ietf.org/html/rfc3339), which does not support\n fractional seconds, thus they must be stripped in the response. This can\n cause issues when the underlying database contains fields that do include\n microseconds, as filters may return unexpected results.\n \"\"\"\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='A meta object containing non-standard information related to the Success')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.Response.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks","title":"ToplevelLinks
","text":" Bases: BaseModel
A set of Links objects, possibly including pagination
Source code inoptimade/models/jsonapi.py
class ToplevelLinks(BaseModel):\n \"\"\"A set of Links objects, possibly including pagination\"\"\"\n\n model_config = ConfigDict(extra=\"allow\")\n\n self: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A link to itself\")\n ] = None\n related: Annotated[\n Optional[JsonLinkType], StrictField(description=\"A related resource link\")\n ] = None\n\n # Pagination\n first: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The first page of data\")\n ] = None\n last: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The last page of data\")\n ] = None\n prev: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The previous page of data\")\n ] = None\n next: Annotated[\n Optional[JsonLinkType], StrictField(description=\"The next page of data\")\n ] = None\n\n @model_validator(mode=\"after\")\n def check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.first","title":"first: Annotated[Optional[JsonLinkType], StrictField(description='The first page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.last","title":"last: Annotated[Optional[JsonLinkType], StrictField(description='The last page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.next","title":"next: Annotated[Optional[JsonLinkType], StrictField(description='The next page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.prev","title":"prev: Annotated[Optional[JsonLinkType], StrictField(description='The previous page of data')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.related","title":"related: Annotated[Optional[JsonLinkType], StrictField(description='A related resource link')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.self","title":"self: Annotated[Optional[JsonLinkType], StrictField(description='A link to itself')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.ToplevelLinks.check_additional_keys_are_links","title":"check_additional_keys_are_links()
","text":"The ToplevelLinks
class allows any additional keys, as long as they are also Links or Urls themselves.
optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_additional_keys_are_links(self) -> \"ToplevelLinks\":\n \"\"\"The `ToplevelLinks` class allows any additional keys, as long as\n they are also Links or Urls themselves.\n\n \"\"\"\n for field, value in self:\n if field not in self.model_fields:\n setattr(\n self,\n field,\n TypeAdapter(Optional[JsonLinkType]).validate_python(value),\n )\n\n return self\n
"},{"location":"api_reference/models/jsonapi/#optimade.models.jsonapi.resource_json_schema_extra","title":"resource_json_schema_extra(schema, model)
","text":"Ensure id
and type
are the first two entries in the list required properties.
This requires that id
and type
are the first model fields defined for all sub-models of BaseResource
.
optimade/models/jsonapi.py
def resource_json_schema_extra(\n schema: dict[str, Any], model: type[\"BaseResource\"]\n) -> None:\n \"\"\"Ensure `id` and `type` are the first two entries in the list required properties.\n\n Note:\n This _requires_ that `id` and `type` are the _first_ model fields defined\n for all sub-models of `BaseResource`.\n\n \"\"\"\n if \"id\" not in schema.get(\"required\", []):\n schema[\"required\"] = [\"id\"] + schema.get(\"required\", [])\n if \"type\" not in schema.get(\"required\", []):\n required = []\n for field in schema.get(\"required\", []):\n required.append(field)\n if field == \"id\":\n # To make sure the property order match the listed properties,\n # this ensures \"type\" is added immediately after \"id\".\n required.append(\"type\")\n schema[\"required\"] = required\n
"},{"location":"api_reference/models/links/","title":"links","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate","title":"Aggregate
","text":" Bases: Enum
Enumeration of aggregate values
Source code inoptimade/models/links.py
class Aggregate(Enum):\n \"\"\"Enumeration of aggregate values\"\"\"\n\n OK = \"ok\"\n TEST = \"test\"\n STAGING = \"staging\"\n NO = \"no\"\n
"},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.NO","title":"NO = 'no'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.OK","title":"OK = 'ok'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.STAGING","title":"STAGING = 'staging'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.Aggregate.TEST","title":"TEST = 'test'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType","title":"LinkType
","text":" Bases: Enum
Enumeration of link_type values
Source code inoptimade/models/links.py
class LinkType(Enum):\n \"\"\"Enumeration of link_type values\"\"\"\n\n CHILD = \"child\"\n ROOT = \"root\"\n EXTERNAL = \"external\"\n PROVIDERS = \"providers\"\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinkType.CHILD","title":"CHILD = 'child'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.EXTERNAL","title":"EXTERNAL = 'external'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.PROVIDERS","title":"PROVIDERS = 'providers'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinkType.ROOT","title":"ROOT = 'root'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource","title":"LinksResource
","text":" Bases: EntryResource
A Links endpoint resource object
Source code inoptimade/models/links.py
class LinksResource(EntryResource):\n \"\"\"A Links endpoint resource object\"\"\"\n\n type: Annotated[\n Literal[\"links\"],\n StrictField(\n description=\"These objects are described in detail in the section Links Endpoint\",\n pattern=\"^links$\",\n ),\n ] = \"links\"\n\n attributes: Annotated[\n LinksResourceAttributes,\n StrictField(\n description=\"A dictionary containing key-value pairs representing the Links resource's properties.\",\n ),\n ]\n\n @model_validator(mode=\"after\")\n def relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.attributes","title":"attributes: Annotated[LinksResourceAttributes, StrictField(description=\"A dictionary containing key-value pairs representing the Links resource's properties.\")]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.type","title":"type: Annotated[Literal['links'], StrictField(description='These objects are described in detail in the section Links Endpoint', pattern='^links$')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResource.relationships_must_not_be_present","title":"relationships_must_not_be_present()
","text":"Source code in optimade/models/links.py
@model_validator(mode=\"after\")\ndef relationships_must_not_be_present(self) -> \"LinksResource\":\n if self.relationships or \"relationships\" in self.model_fields_set:\n raise ValueError('\"relationships\" is not allowed for links resources')\n return self\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes","title":"LinksResourceAttributes
","text":" Bases: Attributes
Links endpoint resource object attributes
Source code inoptimade/models/links.py
class LinksResourceAttributes(Attributes):\n \"\"\"Links endpoint resource object attributes\"\"\"\n\n name: Annotated[\n str,\n StrictField(\n description=\"Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.\",\n ),\n ]\n description: Annotated[\n str,\n StrictField(\n description=\"Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.\",\n ),\n ]\n base_url: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to the base URL for this implementation\",\n ),\n ]\n\n homepage: Annotated[\n Optional[JsonLinkType],\n StrictField(\n description=\"JSON API links object, pointing to a homepage URL for this implementation\",\n ),\n ]\n\n link_type: Annotated[\n LinkType,\n StrictField(\n title=\"Link Type\",\n description=\"\"\"The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\"\"\",\n ),\n ]\n\n aggregate: Annotated[\n Optional[Aggregate],\n StrictField(\n title=\"Aggregate\",\n description=\"\"\"A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.\"\"\",\n ),\n ] = Aggregate.OK\n\n no_aggregate_reason: Annotated[\n Optional[str],\n StrictField(\n description=\"\"\"An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`.\"\"\",\n ),\n ] = None\n
"},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.aggregate","title":"aggregate: Annotated[Optional[Aggregate], StrictField(title=Aggregate, description='A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\\n\\nIf not specified, clients MAY assume that the value is `ok`.\\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\\n\\nSpecific values indicate the reason why the server is providing the suggestion.\\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\\n\\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.')] = Aggregate.OK
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.base_url","title":"base_url: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to the base URL for this implementation')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.description","title":"description: Annotated[str, StrictField(description='Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user.')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.homepage","title":"homepage: Annotated[Optional[JsonLinkType], StrictField(description='JSON API links object, pointing to a homepage URL for this implementation')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.link_type","title":"link_type: Annotated[LinkType, StrictField(title='Link Type', description=\"The type of the linked relation.\\nMUST be one of these values: 'child', 'root', 'external', 'providers'.\")]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.name","title":"name: Annotated[str, StrictField(description='Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user.')]
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.no_aggregate_reason","title":"no_aggregate_reason: Annotated[Optional[str], StrictField(description='An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\\nIt SHOULD NOT be present if `aggregate`=`ok`.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/links/#optimade.models.links.LinksResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/optimade_json/","title":"optimade_json","text":"Modified JSON API v1.0 for OPTIMADE API
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ValidIdentifier","title":"ValidIdentifier = Annotated[str, Field(pattern=IDENTIFIER_REGEX)]
module-attribute
","text":"A type that constrains strings to valid OPTIMADE identifiers (e.g., property names, ID strings).
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta","title":"BaseRelationshipMeta
","text":" Bases: Meta
Specific meta field for base relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipMeta(jsonapi.Meta):\n \"\"\"Specific meta field for base relationship resource\"\"\"\n\n description: Annotated[\n str,\n StrictField(\n description=\"OPTIONAL human-readable description of the relationship.\"\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta.description","title":"description: Annotated[str, StrictField(description='OPTIONAL human-readable description of the relationship.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource","title":"BaseRelationshipResource
","text":" Bases: BaseResource
Minimum requirements to represent a relationship resource
Source code inoptimade/models/optimade_json.py
class BaseRelationshipResource(jsonapi.BaseResource):\n \"\"\"Minimum requirements to represent a relationship resource\"\"\"\n\n meta: Annotated[\n Optional[BaseRelationshipMeta],\n StrictField(\n description=\"Relationship meta field. MUST contain 'description' if supplied.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.id","title":"id: Annotated[str, StrictField(description='Resource ID')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.meta","title":"meta: Annotated[Optional[BaseRelationshipMeta], StrictField(description=\"Relationship meta field. MUST contain 'description' if supplied.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.BaseRelationshipResource.type","title":"type: Annotated[str, StrictField(description='Resource type')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType","title":"DataType
","text":" Bases: Enum
Optimade Data types
See the section \"Data types\" in the OPTIMADE API specification for more information.
Source code inoptimade/models/optimade_json.py
class DataType(Enum):\n \"\"\"Optimade Data types\n\n See the section \"Data types\" in the OPTIMADE API specification for more information.\n \"\"\"\n\n STRING = \"string\"\n INTEGER = \"integer\"\n FLOAT = \"float\"\n BOOLEAN = \"boolean\"\n TIMESTAMP = \"timestamp\"\n LIST = \"list\"\n DICTIONARY = \"dictionary\"\n UNKNOWN = \"unknown\"\n\n @classmethod\n def get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n\n @classmethod\n def from_python_type(\n cls, python_type: Union[type, str, object]\n ) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n\n @classmethod\n def from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.BOOLEAN","title":"BOOLEAN = 'boolean'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.DICTIONARY","title":"DICTIONARY = 'dictionary'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.FLOAT","title":"FLOAT = 'float'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.INTEGER","title":"INTEGER = 'integer'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.LIST","title":"LIST = 'list'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.STRING","title":"STRING = 'string'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.TIMESTAMP","title":"TIMESTAMP = 'timestamp'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.UNKNOWN","title":"UNKNOWN = 'unknown'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.from_json_type","title":"from_json_type(json_type)
classmethod
","text":"Get OPTIMADE data type from a named JSON type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_json_type(cls, json_type: str) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a named JSON type\"\"\"\n mapping = {\n \"string\": cls.STRING,\n \"integer\": cls.INTEGER,\n \"number\": cls.FLOAT, # actually includes both integer and float\n \"object\": cls.DICTIONARY,\n \"array\": cls.LIST,\n \"boolean\": cls.BOOLEAN,\n \"null\": cls.UNKNOWN,\n # OpenAPI \"format\"s:\n \"double\": cls.FLOAT,\n \"float\": cls.FLOAT,\n \"int32\": cls.INTEGER,\n \"int64\": cls.INTEGER,\n \"date\": cls.TIMESTAMP,\n \"date-time\": cls.TIMESTAMP,\n \"password\": cls.STRING,\n \"byte\": cls.STRING,\n \"binary\": cls.STRING,\n # Non-OpenAPI \"format\"s, but may still be used by pydantic/FastAPI\n \"email\": cls.STRING,\n \"uuid\": cls.STRING,\n \"uri\": cls.STRING,\n \"hostname\": cls.STRING,\n \"ipv4\": cls.STRING,\n \"ipv6\": cls.STRING,\n }\n\n return mapping.get(json_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.from_python_type","title":"from_python_type(python_type)
classmethod
","text":"Get OPTIMADE data type from a Python type
Source code inoptimade/models/optimade_json.py
@classmethod\ndef from_python_type(\n cls, python_type: Union[type, str, object]\n) -> Optional[\"DataType\"]:\n \"\"\"Get OPTIMADE data type from a Python type\"\"\"\n mapping = {\n \"bool\": cls.BOOLEAN,\n \"int\": cls.INTEGER,\n \"float\": cls.FLOAT,\n \"complex\": None,\n \"generator\": cls.LIST,\n \"list\": cls.LIST,\n \"tuple\": cls.LIST,\n \"range\": cls.LIST,\n \"hash\": cls.INTEGER,\n \"str\": cls.STRING,\n \"bytes\": cls.STRING,\n \"bytearray\": None,\n \"memoryview\": None,\n \"set\": cls.LIST,\n \"frozenset\": cls.LIST,\n \"dict\": cls.DICTIONARY,\n \"dict_keys\": cls.LIST,\n \"dict_values\": cls.LIST,\n \"dict_items\": cls.LIST,\n \"Nonetype\": cls.UNKNOWN,\n \"None\": cls.UNKNOWN,\n \"datetime\": cls.TIMESTAMP,\n \"date\": cls.TIMESTAMP,\n \"time\": cls.TIMESTAMP,\n \"datetime.datetime\": cls.TIMESTAMP,\n \"datetime.date\": cls.TIMESTAMP,\n \"datetime.time\": cls.TIMESTAMP,\n }\n\n if isinstance(python_type, type):\n python_type = python_type.__name__\n elif isinstance(python_type, object):\n if str(python_type) in mapping:\n python_type = str(python_type)\n else:\n python_type = type(python_type).__name__\n\n return mapping.get(python_type, None)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.DataType.get_values","title":"get_values()
classmethod
","text":"Get OPTIMADE data types (enum values) as a (sorted) list
Source code inoptimade/models/optimade_json.py
@classmethod\ndef get_values(cls) -> list[str]:\n \"\"\"Get OPTIMADE data types (enum values) as a (sorted) list\"\"\"\n return sorted(_.value for _ in cls)\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation","title":"Implementation
","text":" Bases: BaseModel
Information on the server implementation
Source code inoptimade/models/optimade_json.py
class Implementation(BaseModel):\n \"\"\"Information on the server implementation\"\"\"\n\n name: Annotated[\n Optional[str], StrictField(description=\"name of the implementation\")\n ] = None\n\n version: Annotated[\n Optional[str],\n StrictField(description=\"version string of the current implementation\"),\n ] = None\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.\",\n ),\n ] = None\n\n source_url: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.\",\n ),\n ] = None\n\n maintainer: Annotated[\n Optional[ImplementationMaintainer],\n StrictField(\n description=\"A dictionary providing details about the maintainer of the implementation.\",\n ),\n ] = None\n\n issue_tracker: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.issue_tracker","title":"issue_tracker: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description=\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.\")] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.maintainer","title":"maintainer: Annotated[Optional[ImplementationMaintainer], StrictField(description='A dictionary providing details about the maintainer of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.name","title":"name: Annotated[Optional[str], StrictField(description='name of the implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.source_url","title":"source_url: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Implementation.version","title":"version: Annotated[Optional[str], StrictField(description='version string of the current implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ImplementationMaintainer","title":"ImplementationMaintainer
","text":" Bases: BaseModel
Details about the maintainer of the implementation
Source code inoptimade/models/optimade_json.py
class ImplementationMaintainer(BaseModel):\n \"\"\"Details about the maintainer of the implementation\"\"\"\n\n email: Annotated[\n EmailStr, StrictField(description=\"the maintainer's email address\")\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ImplementationMaintainer.email","title":"email: Annotated[EmailStr, StrictField(description=\"the maintainer's email address\")]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError","title":"OptimadeError
","text":" Bases: Error
detail MUST be present
Source code inoptimade/models/optimade_json.py
class OptimadeError(jsonapi.Error):\n \"\"\"detail MUST be present\"\"\"\n\n detail: Annotated[\n str,\n StrictField(\n description=\"A human-readable explanation specific to this occurrence of the problem.\",\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.OptimadeError.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider","title":"Provider
","text":" Bases: BaseModel
Information on the database provider of the implementation.
Source code inoptimade/models/optimade_json.py
class Provider(BaseModel):\n \"\"\"Information on the database provider of the implementation.\"\"\"\n\n name: Annotated[\n str, StrictField(description=\"a short name for the database provider\")\n ]\n\n description: Annotated[\n str, StrictField(description=\"a longer description of the database provider\")\n ]\n\n prefix: Annotated[\n str,\n StrictField(\n pattern=r\"^[a-z]([a-z]|[0-9]|_)*$\",\n description=\"database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.\",\n ),\n ]\n\n homepage: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n description=\"a [JSON API links object](http://jsonapi.org/format/1.0#document-links) \"\n \"pointing to homepage of the database provider, either \"\n \"directly as a string, or as a link object.\",\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.description","title":"description: Annotated[str, StrictField(description='a longer description of the database provider')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.homepage","title":"homepage: Annotated[Optional[jsonapi.JsonLinkType], StrictField(description='a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.name","title":"name: Annotated[str, StrictField(description='a short name for the database provider')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Provider.prefix","title":"prefix: Annotated[str, StrictField(pattern='^[a-z]([a-z]|[0-9]|_)*$', description='database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship","title":"Relationship
","text":" Bases: Relationship
Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.
Source code inoptimade/models/optimade_json.py
class Relationship(jsonapi.Relationship):\n \"\"\"Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.\"\"\"\n\n data: Annotated[\n Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]],\n StrictField(description=\"Resource linkage\", uniqueItems=True),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.data","title":"data: Annotated[Optional[Union[BaseRelationshipResource, list[BaseRelationshipResource]]], StrictField(description='Resource linkage', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.links","title":"links: Annotated[Optional[RelationshipLinks], StrictField(description='a links object containing at least one of the following: self, related')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object that contains non-standard meta-information about the relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Relationship.at_least_one_relationship_key_must_be_set","title":"at_least_one_relationship_key_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef at_least_one_relationship_key_must_be_set(self) -> \"Relationship\":\n if self.links is None and self.data is None and self.meta is None:\n raise ValueError(\n \"Either 'links', 'data', or 'meta' MUST be specified for Relationship\"\n )\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta","title":"ResponseMeta
","text":" Bases: Meta
A JSON API meta member that contains JSON API meta objects of non-standard meta-information.
OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.
Source code inoptimade/models/optimade_json.py
class ResponseMeta(jsonapi.Meta):\n \"\"\"\n A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\n that contains JSON API meta objects of non-standard\n meta-information.\n\n OPTIONAL additional information global to the query that is not\n specified in this document, MUST start with a\n database-provider-specific prefix.\n \"\"\"\n\n query: Annotated[\n ResponseMetaQuery,\n StrictField(description=\"Information on the Query that was requested\"),\n ]\n\n api_version: Annotated[\n SemanticVersion,\n StrictField(\n description=\"\"\"Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.\"\"\",\n ),\n ]\n\n more_data_available: Annotated[\n bool,\n StrictField(\n description=\"`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).\",\n ),\n ]\n\n # start of \"SHOULD\" fields for meta response\n optimade_schema: Annotated[\n Optional[jsonapi.JsonLinkType],\n StrictField(\n alias=\"schema\",\n description=\"\"\"A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.\"\"\",\n ),\n ] = None\n\n time_stamp: Annotated[\n Optional[datetime],\n StrictField(\n description=\"A timestamp containing the date and time at which the query was executed.\",\n ),\n ] = None\n\n data_returned: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.\",\n ge=0,\n ),\n ] = None\n\n provider: Annotated[\n Optional[Provider],\n StrictField(\n description=\"information on the database provider of the implementation.\"\n ),\n ] = None\n\n # start of \"MAY\" fields for meta response\n data_available: Annotated[\n Optional[int],\n StrictField(\n description=\"An integer containing the total number of data resource objects available in the database for the endpoint.\",\n ),\n ] = None\n\n last_id: Annotated[\n Optional[str],\n StrictField(description=\"a string containing the last ID returned\"),\n ] = None\n\n response_message: Annotated[\n Optional[str], StrictField(description=\"response string from the server\")\n ] = None\n\n implementation: Annotated[\n Optional[Implementation],\n StrictField(description=\"a dictionary describing the server implementation\"),\n ] = None\n\n warnings: Annotated[\n Optional[list[Warnings]],\n StrictField(\n description=\"\"\"A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects.\"\"\",\n uniqueItems=True,\n ),\n ] = None\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.api_version","title":"api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\\nExamples: `1.0.0`, `1.0.0-rc.2`.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.data_available","title":"data_available: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects available in the database for the endpoint.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.data_returned","title":"data_returned: Annotated[Optional[int], StrictField(description='An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.', ge=0)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.implementation","title":"implementation: Annotated[Optional[Implementation], StrictField(description='a dictionary describing the server implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.last_id","title":"last_id: Annotated[Optional[str], StrictField(description='a string containing the last ID returned')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.more_data_available","title":"more_data_available: Annotated[bool, StrictField(description='`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.optimade_schema","title":"optimade_schema: Annotated[Optional[jsonapi.JsonLinkType], StrictField(alias=schema, description='A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\\nIt is possible that future versions of this specification allows for alternative schema types.\\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.provider","title":"provider: Annotated[Optional[Provider], StrictField(description='information on the database provider of the implementation.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.query","title":"query: Annotated[ResponseMetaQuery, StrictField(description='Information on the Query that was requested')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.response_message","title":"response_message: Annotated[Optional[str], StrictField(description='response string from the server')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.time_stamp","title":"time_stamp: Annotated[Optional[datetime], StrictField(description='A timestamp containing the date and time at which the query was executed.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMeta.warnings","title":"warnings: Annotated[Optional[list[Warnings]], StrictField(description='A list of warning resource objects representing non-critical errors or warnings.\\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\\nThis is an exclusive field for error resource objects.', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMetaQuery","title":"ResponseMetaQuery
","text":" Bases: BaseModel
Information on the query that was requested.
Source code inoptimade/models/optimade_json.py
class ResponseMetaQuery(BaseModel):\n \"\"\"Information on the query that was requested.\"\"\"\n\n representation: Annotated[\n str,\n StrictField(\n description=\"\"\"A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`\"\"\",\n ),\n ]\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.ResponseMetaQuery.representation","title":"representation: Annotated[str, StrictField(description='A string with the part of the URL following the versioned or unversioned base URL that serves the API.\\nQuery parameters that have not been used in processing the request MAY be omitted.\\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\\nExample: `/structures?filter=nelements=2`')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success","title":"Success
","text":" Bases: Response
errors are not allowed
Source code inoptimade/models/optimade_json.py
class Success(jsonapi.Response):\n \"\"\"errors are not allowed\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information\"),\n ]\n\n @model_validator(mode=\"after\")\n def either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Success.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings","title":"Warnings
","text":" Bases: OptimadeError
OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.
From the specification:
A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.
Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.
Source code inoptimade/models/optimade_json.py
class Warnings(OptimadeError):\n \"\"\"OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\n From the specification:\n\n A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\n The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\n Note: Must be named \"Warnings\", since \"Warning\" is a built-in Python class.\n\n \"\"\"\n\n model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)\n\n type: Annotated[\n Literal[\"warning\"],\n StrictField(\n description='Warnings must be of type \"warning\"',\n pattern=\"^warning$\",\n ),\n ] = \"warning\"\n\n @model_validator(mode=\"after\")\n def status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.code","title":"code: Annotated[Optional[str], StrictField(description='an application-specific error code, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.detail","title":"detail: Annotated[str, StrictField(description='A human-readable explanation specific to this occurrence of the problem.')]
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.id","title":"id: Annotated[Optional[str], StrictField(description='A unique identifier for this particular occurrence of the problem.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.links","title":"links: Annotated[Optional[ErrorLinks], StrictField(description='A links object storing about')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about the error.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.model_config","title":"model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.source","title":"source: Annotated[Optional[ErrorSource], StrictField(description='An object containing references to the source of the error')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.status","title":"status: Annotated[Optional[Annotated[str, BeforeValidator(str)]], StrictField(description='the HTTP status code applicable to this problem, expressed as a string value.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.title","title":"title: Annotated[Optional[str], StrictField(description='A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.type","title":"type: Annotated[Literal['warning'], StrictField(description='Warnings must be of type \"warning\"', pattern='^warning$')] = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.__hash__","title":"__hash__()
","text":"Source code in optimade/models/jsonapi.py
def __hash__(self):\n return hash(self.model_dump_json())\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.Warnings.status_must_not_be_specified","title":"status_must_not_be_specified()
","text":"Source code in optimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef status_must_not_be_specified(self) -> \"Warnings\":\n if self.status or \"status\" in self.model_fields_set:\n raise ValueError(\"status MUST NOT be specified for warnings\")\n return self\n
"},{"location":"api_reference/models/optimade_json/#optimade.models.optimade_json.warnings_json_schema_extra","title":"warnings_json_schema_extra(schema, model)
","text":"Update OpenAPI JSON schema model for Warning
.
type
is in the list required properties and in the correct place.status
property. This property is not allowed for Warning
, nor is it a part of the OPTIMADE definition of the Warning
object.Since type
is the last model field defined, it will simply be appended.
optimade/models/optimade_json.py
def warnings_json_schema_extra(schema: dict[str, Any], model: type[\"Warnings\"]) -> None:\n \"\"\"Update OpenAPI JSON schema model for `Warning`.\n\n * Ensure `type` is in the list required properties and in the correct place.\n * Remove `status` property.\n This property is not allowed for `Warning`, nor is it a part of the OPTIMADE\n definition of the `Warning` object.\n\n Note:\n Since `type` is the _last_ model field defined, it will simply be appended.\n\n \"\"\"\n if \"required\" in schema:\n if \"type\" not in schema[\"required\"]:\n schema[\"required\"].append(\"type\")\n else:\n schema[\"required\"] = [\"type\"]\n schema.get(\"properties\", {}).pop(\"status\", None)\n
"},{"location":"api_reference/models/references/","title":"references","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person","title":"Person
","text":" Bases: BaseModel
A person, i.e., an author, editor or other.
Source code inoptimade/models/references.py
class Person(BaseModel):\n \"\"\"A person, i.e., an author, editor or other.\"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Full name of the person, REQUIRED.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n firstname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"First name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n lastname: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Last name of the person.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"api_reference/models/references/#optimade.models.references.Person.firstname","title":"firstname: Annotated[Optional[str], OptimadeField(description='First name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person.lastname","title":"lastname: Annotated[Optional[str], OptimadeField(description='Last name of the person.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.Person.name","title":"name: Annotated[str, OptimadeField(description='Full name of the person, REQUIRED.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource","title":"ReferenceResource
","text":" Bases: EntryResource
The references
entries describe bibliographic references.
The following properties are used to provide the bibliographic details:
null
.optimade/models/references.py
class ReferenceResource(EntryResource):\n \"\"\"The `references` entries describe bibliographic references.\n\n The following properties are used to provide the bibliographic details:\n\n - **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n - **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n - **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n - **doi** and **url**: values are strings.\n - **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties.\n\n \"\"\"\n\n type: Annotated[\n Literal[\"references\"],\n OptimadeField(\n description=\"\"\"The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n- **Example**: `\"structures\"`\"\"\",\n pattern=\"^references$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"references\"\n attributes: ReferenceResourceAttributes\n\n @field_validator(\"attributes\", mode=\"before\")\n @classmethod\n def validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.attributes","title":"attributes: ReferenceResourceAttributes
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.type","title":"type: Annotated[Literal['references'], OptimadeField(description='The name of the type of an entry.\\n- **Type**: string.\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type <type> and ID <id> MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n- **Example**: `\"structures\"`', pattern='^references$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResource.validate_attributes","title":"validate_attributes(value)
classmethod
","text":"Source code in optimade/models/references.py
@field_validator(\"attributes\", mode=\"before\")\n@classmethod\ndef validate_attributes(cls, value: Any) -> dict[str, Any]:\n if not isinstance(value, dict):\n if isinstance(value, BaseModel):\n value = value.model_dump()\n else:\n raise TypeError(\"attributes field must be a mapping\")\n if not any(prop[1] is not None for prop in value):\n raise ValueError(\"reference object must have at least one field defined\")\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes","title":"ReferenceResourceAttributes
","text":" Bases: EntryResourceAttributes
Model that stores the attributes of a reference.
Many properties match the meaning described in the BibTeX specification.
Source code inoptimade/models/references.py
class ReferenceResourceAttributes(EntryResourceAttributes):\n \"\"\"Model that stores the attributes of a reference.\n\n Many properties match the meaning described in the\n [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf).\n\n \"\"\"\n\n authors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the authors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n editors: Annotated[\n Optional[list[Person]],\n OptimadeField(\n description=\"List of person objects containing the editors of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n doi: Annotated[\n Optional[str],\n OptimadeField(\n description=\"The digital object identifier of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n url: Annotated[\n Optional[AnyUrl],\n OptimadeField(\n description=\"The URL of the reference.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n address: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n annote: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n booktitle: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n chapter: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n crossref: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n edition: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n howpublished: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n institution: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n journal: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n key: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n month: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n note: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n number: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n organization: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n pages: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n publisher: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n school: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n series: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n title: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n bib_type: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Type of the reference, corresponding to the **type** property in the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n volume: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n year: Annotated[\n Optional[str],\n OptimadeField(\n description=\"Meaning of property matches the BiBTeX specification.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.address","title":"address: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.annote","title":"annote: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.authors","title":"authors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the authors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.bib_type","title":"bib_type: Annotated[Optional[str], OptimadeField(description='Type of the reference, corresponding to the **type** property in the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.booktitle","title":"booktitle: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.chapter","title":"chapter: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.crossref","title":"crossref: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.doi","title":"doi: Annotated[Optional[str], OptimadeField(description='The digital object identifier of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.edition","title":"edition: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.editors","title":"editors: Annotated[Optional[list[Person]], OptimadeField(description='List of person objects containing the editors of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.howpublished","title":"howpublished: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.institution","title":"institution: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.journal","title":"journal: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.key","title":"key: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.month","title":"month: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.note","title":"note: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.number","title":"number: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.organization","title":"organization: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.pages","title":"pages: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.publisher","title":"publisher: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.school","title":"school: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.series","title":"series: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.title","title":"title: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.url","title":"url: Annotated[Optional[AnyUrl], OptimadeField(description='The URL of the reference.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.volume","title":"volume: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.year","title":"year: Annotated[Optional[str], OptimadeField(description='Meaning of property matches the BiBTeX specification.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/references/#optimade.models.references.ReferenceResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/responses/","title":"responses","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse","title":"EntryInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class EntryInfoResponse(Success):\n data: Annotated[\n EntryInfoResource,\n StrictField(description=\"OPTIMADE information for an entry endpoint.\"),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.data","title":"data: Annotated[EntryInfoResource, StrictField(description='OPTIMADE information for an entry endpoint.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany","title":"EntryResponseMany
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseMany(Success):\n data: Annotated[ # type: ignore[assignment]\n Union[list[EntryResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.data","title":"data: Annotated[Union[list[EntryResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne","title":"EntryResponseOne
","text":" Bases: Success
optimade/models/responses.py
class EntryResponseOne(Success):\n data: Annotated[\n Optional[Union[EntryResource, dict[str, Any]]],\n StrictField(\n description=\"The single entry resource returned by this query.\",\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n included: Annotated[\n Optional[Union[list[EntryResource], list[dict[str, Any]]]],\n StrictField(\n description=\"A list of unique included OPTIMADE entry resources.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ] = None # type: ignore[assignment]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.data","title":"data: Annotated[Optional[Union[EntryResource, dict[str, Any]]], StrictField(description='The single entry resource returned by this query.', union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.EntryResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse","title":"ErrorResponse
","text":" Bases: Response
errors MUST be present and data MUST be skipped
Source code inoptimade/models/responses.py
class ErrorResponse(Response):\n \"\"\"errors MUST be present and data MUST be skipped\"\"\"\n\n meta: Annotated[\n ResponseMeta,\n StrictField(description=\"A meta object containing non-standard information.\"),\n ]\n errors: Annotated[\n list[OptimadeError],\n StrictField(\n description=\"A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.\",\n uniqueItems=True,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.data","title":"data: Annotated[Optional[Union[None, Resource, list[Resource]]], StrictField(description='Outputted Data', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.errors","title":"errors: Annotated[list[OptimadeError], StrictField(description='A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.', uniqueItems=True)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.data_must_be_skipped","title":"data_must_be_skipped()
","text":"Source code in optimade/models/responses.py
@model_validator(mode=\"after\")\ndef data_must_be_skipped(self) -> \"ErrorResponse\":\n if self.data or \"data\" in self.model_fields_set:\n raise ValueError(\"data MUST be skipped for failures reporting errors.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ErrorResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Response\":\n required_fields = (\"data\", \"meta\", \"errors\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response\"\n )\n if \"errors\" in self.model_fields_set and not self.errors:\n raise ValueError(\"Errors MUST NOT be an empty or 'null' value.\")\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse","title":"IndexInfoResponse
","text":" Bases: Success
optimade/models/responses.py
class IndexInfoResponse(Success):\n data: Annotated[\n IndexInfoResource, StrictField(description=\"Index meta-database /info data.\")\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.data","title":"data: Annotated[IndexInfoResource, StrictField(description='Index meta-database /info data.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.IndexInfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse","title":"InfoResponse
","text":" Bases: Success
optimade/models/responses.py
class InfoResponse(Success):\n data: Annotated[\n BaseInfoResource, StrictField(description=\"The implementations /info data.\")\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.data","title":"data: Annotated[BaseInfoResource, StrictField(description='The implementations /info data.')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.included","title":"included: Annotated[Optional[list[Resource]], StrictField(description='A list of unique included resources', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.InfoResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse","title":"LinksResponse
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class LinksResponse(EntryResponseMany):\n data: Annotated[\n Union[list[LinksResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE links resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.data","title":"data: Annotated[Union[list[LinksResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE links resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.LinksResponse.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany","title":"ReferenceResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class ReferenceResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[ReferenceResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE references entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.data","title":"data: Annotated[Union[list[ReferenceResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE references entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne","title":"ReferenceResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class ReferenceResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[ReferenceResource, dict[str, Any]]],\n StrictField(\n description=\"A single references entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.data","title":"data: Annotated[Optional[Union[ReferenceResource, dict[str, Any]]], StrictField(description='A single references entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.ReferenceResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany","title":"StructureResponseMany
","text":" Bases: EntryResponseMany
optimade/models/responses.py
class StructureResponseMany(EntryResponseMany):\n data: Annotated[\n Union[list[StructureResource], list[dict[str, Any]]],\n StrictField(\n description=\"List of unique OPTIMADE structures entry resource objects.\",\n uniqueItems=True,\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.data","title":"data: Annotated[Union[list[StructureResource], list[dict[str, Any]]], StrictField(description='List of unique OPTIMADE structures entry resource objects.', uniqueItems=True, union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseMany.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne","title":"StructureResponseOne
","text":" Bases: EntryResponseOne
optimade/models/responses.py
class StructureResponseOne(EntryResponseOne):\n data: Annotated[\n Optional[Union[StructureResource, dict[str, Any]]],\n StrictField(\n description=\"A single structures entry resource.\",\n union_mode=\"left_to_right\",\n ),\n ]\n
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.data","title":"data: Annotated[Optional[Union[StructureResource, dict[str, Any]]], StrictField(description='A single structures entry resource.', union_mode=left_to_right)]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.errors","title":"errors: Annotated[Optional[list[Error]], StrictField(description='A list of unique errors', uniqueItems=True)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.included","title":"included: Annotated[Optional[Union[list[EntryResource], list[dict[str, Any]]]], StrictField(description='A list of unique included OPTIMADE entry resources.', uniqueItems=True, union_mode=left_to_right)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.jsonapi","title":"jsonapi: Annotated[Optional[JsonApi], StrictField(description='Information about the JSON API used')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.links","title":"links: Annotated[Optional[ToplevelLinks], StrictField(description='Links associated with the primary data or errors')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.meta","title":"meta: Annotated[ResponseMeta, StrictField(description='A meta object containing non-standard information')]
instance-attribute
","text":""},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.model_config","title":"model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')})
class-attribute
instance-attribute
","text":"The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.
"},{"location":"api_reference/models/responses/#optimade.models.responses.StructureResponseOne.either_data_meta_or_errors_must_be_set","title":"either_data_meta_or_errors_must_be_set()
","text":"Overwriting the existing validation function, since 'errors' MUST NOT be set.
Source code inoptimade/models/optimade_json.py
@model_validator(mode=\"after\")\ndef either_data_meta_or_errors_must_be_set(self) -> \"Success\":\n \"\"\"Overwriting the existing validation function, since 'errors' MUST NOT be set.\"\"\"\n required_fields = (\"data\", \"meta\")\n if not any(field in self.model_fields_set for field in required_fields):\n raise ValueError(\n f\"At least one of {required_fields} MUST be specified in the top-level response.\"\n )\n\n # errors MUST be skipped\n if self.errors or \"errors\" in self.model_fields_set:\n raise ValueError(\"'errors' MUST be skipped for a successful response.\")\n\n return self\n
"},{"location":"api_reference/models/structures/","title":"structures","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.CORRELATED_STRUCTURE_FIELDS","title":"CORRELATED_STRUCTURE_FIELDS = ({'dimension_types', 'nperiodic_dimensions'}, {'cartesian_site_positions', 'species_at_sites'}, {'nsites', 'cartesian_site_positions'}, {'species_at_sites', 'species'})
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.EPS","title":"EPS = 2 ** -23
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Vector3D","title":"Vector3D = Annotated[list[Annotated[float, BeforeValidator(float)]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Vector3D_unknown","title":"Vector3D_unknown = Annotated[list[Optional[Annotated[float, BeforeValidator(float)]]], Field(min_length=3, max_length=3)]
module-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly","title":"Assembly
","text":" Bases: BaseModel
A description of groups of sites that are statistically correlated.
{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}
: the first site and the second site never occur at the same time in the unit cell. Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}
: the second and third site are either present together or not present; they form the first group of atoms for this assembly. The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site. 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).optimade/models/structures.py
class Assembly(BaseModel):\n \"\"\"A description of groups of sites that are statistically correlated.\n\n - **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n \"\"\"\n\n sites_in_groups: Annotated[\n list[list[int]],\n OptimadeField(\n description=\"\"\"Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n group_probabilities: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n @field_validator(\"sites_in_groups\", mode=\"after\")\n @classmethod\n def validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.group_probabilities","title":"group_probabilities: Annotated[list[float], OptimadeField(description='Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\nIt SHOULD sum to one.\\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.sites_in_groups","title":"sites_in_groups: Annotated[list[list[int]], OptimadeField(description='Index of the sites (0-based) that belong to each group for each assembly.\\n\\n- **Examples**:\\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.check_self_consistency","title":"check_self_consistency()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_self_consistency(self) -> \"Assembly\":\n if len(self.group_probabilities) != len(self.sites_in_groups):\n raise ValueError(\n f\"sites_in_groups and group_probabilities MUST be of same length, \"\n f\"but are {len(self.sites_in_groups)} and {len(self.group_probabilities)}, \"\n \"respectively\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Assembly.validate_sites_in_groups","title":"validate_sites_in_groups(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"sites_in_groups\", mode=\"after\")\n@classmethod\ndef validate_sites_in_groups(cls, value: list[list[int]]) -> list[list[int]]:\n sites = []\n for group in value:\n sites.extend(group)\n if len(set(sites)) != len(sites):\n raise ValueError(\n f\"A site MUST NOT appear in more than one group. Given value: {value}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity","title":"Periodicity
","text":" Bases: IntEnum
Integer enumeration of dimension_types values
Source code inoptimade/models/structures.py
class Periodicity(IntEnum):\n \"\"\"Integer enumeration of dimension_types values\"\"\"\n\n APERIODIC = 0\n PERIODIC = 1\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity.APERIODIC","title":"APERIODIC = 0
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Periodicity.PERIODIC","title":"PERIODIC = 1
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species","title":"Species
","text":" Bases: BaseModel
A list describing the species of the sites of this structure.
Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).
[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]
: any site with this species is occupied by a Ti atom.[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]
: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]
: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]
: any site with this species is occupied by a carbon isotope with mass 12.[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]
: any site with this species is occupied by a carbon isotope with mass 13.[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]
: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.optimade/models/structures.py
class Species(BaseModel):\n \"\"\"A list describing the species of the sites of this structure.\n\n Species can represent pure chemical elements, virtual-crystal atoms representing a\n statistical occupation of a given site by multiple chemical elements, and/or a\n location to which there are attached atoms, i.e., atoms whose precise location are\n unknown beyond that they are attached to that position (frequently used to indicate\n hydrogen atoms attached to another element, e.g., a carbon with three attached\n hydrogens might represent a methyl group, -CH3).\n\n - **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\n\n \"\"\"\n\n name: Annotated[\n str,\n OptimadeField(\n description=\"\"\"Gives the name of the species; the **name** value MUST be unique in the `species` list.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n chemical_symbols: Annotated[\n list[ChemicalSymbol],\n OptimadeField(\n description=\"\"\"MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n concentration: Annotated[\n list[float],\n OptimadeField(\n description=\"\"\"MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.OPTIONAL,\n ),\n ]\n\n mass: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.\"\"\",\n unit=\"a.m.u.\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n original_name: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n attached: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nattached: Annotated[\n Optional[list[int]],\n OptimadeField(\n description=\"\"\"If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n @field_validator(\"concentration\", \"mass\", mode=\"after\")\n def validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n ) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n\n @field_validator(\"attached\", \"nattached\", mode=\"after\")\n @classmethod\n def validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n ) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.attached","title":"attached: Annotated[Optional[list[str]], OptimadeField(description='If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.chemical_symbols","title":"chemical_symbols: Annotated[list[ChemicalSymbol], OptimadeField(description='MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\\n\\n- a valid chemical-element symbol, or\\n- the special value `\"X\"` to represent a non-chemical element, or\\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.concentration","title":"concentration: Annotated[list[float], OptimadeField(description='MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\nNote that concentrations are uncorrelated between different site (even of the same species).', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.mass","title":"mass: Annotated[Optional[list[float]], OptimadeField(description='If present MUST be a list of floats expressed in a.m.u.\\nElements denoting vacancies MUST have masses equal to 0.', unit='a.m.u.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.name","title":"name: Annotated[str, OptimadeField(description='Gives the name of the species; the **name** value MUST be unique in the `species` list.', support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.nattached","title":"nattached: Annotated[Optional[list[int]], OptimadeField(description='If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.original_name","title":"original_name: Annotated[Optional[str], OptimadeField(description='Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.Species.attached_nattached_mutually_exclusive","title":"attached_nattached_mutually_exclusive()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef attached_nattached_mutually_exclusive(self) -> \"Species\":\n if (self.attached is None and self.nattached is not None) or (\n self.attached is not None and self.nattached is None\n ):\n raise ValueError(\n f\"Either both or none of attached ({self.attached}) and nattached \"\n f\"({self.nattached}) MUST be set.\"\n )\n\n if (\n self.attached is not None\n and self.nattached is not None\n and len(self.attached) != len(self.nattached)\n ):\n raise ValueError(\n f\"attached ({self.attached}) and nattached ({self.nattached}) MUST be \"\n \"lists of equal length.\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.validate_concentration_and_mass","title":"validate_concentration_and_mass(value, info)
","text":"Source code in optimade/models/structures.py
@field_validator(\"concentration\", \"mass\", mode=\"after\")\ndef validate_concentration_and_mass(\n cls, value: Optional[list[float]], info: \"ValidationInfo\"\n) -> Optional[list[float]]:\n if not value:\n return value\n\n if info.data.get(\"chemical_symbols\"):\n if len(value) != len(info.data[\"chemical_symbols\"]):\n raise ValueError(\n f\"Length of concentration ({len(value)}) MUST equal length of \"\n f\"chemical_symbols ({len(info.data['chemical_symbols'])})\"\n )\n return value\n\n raise ValueError(\n f\"Could not validate {info.field_name!r} as 'chemical_symbols' is missing/invalid.\"\n )\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.Species.validate_minimum_list_length","title":"validate_minimum_list_length(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"attached\", \"nattached\", mode=\"after\")\n@classmethod\ndef validate_minimum_list_length(\n cls, value: Optional[Union[list[str], list[int]]]\n) -> Optional[Union[list[str], list[int]]]:\n if value is not None and len(value) < 1:\n raise ValueError(\n \"The list's length MUST be 1 or more, instead it was found to be \"\n f\"{len(value)}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures","title":"StructureFeatures
","text":" Bases: Enum
Enumeration of structure_features values
Source code inoptimade/models/structures.py
class StructureFeatures(Enum):\n \"\"\"Enumeration of structure_features values\"\"\"\n\n DISORDER = \"disorder\"\n IMPLICIT_ATOMS = \"implicit_atoms\"\n SITE_ATTACHMENTS = \"site_attachments\"\n ASSEMBLIES = \"assemblies\"\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.ASSEMBLIES","title":"ASSEMBLIES = 'assemblies'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.DISORDER","title":"DISORDER = 'disorder'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.IMPLICIT_ATOMS","title":"IMPLICIT_ATOMS = 'implicit_atoms'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureFeatures.SITE_ATTACHMENTS","title":"SITE_ATTACHMENTS = 'site_attachments'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource","title":"StructureResource
","text":" Bases: EntryResource
Representing a structure.
Source code inoptimade/models/structures.py
class StructureResource(EntryResource):\n \"\"\"Representing a structure.\"\"\"\n\n type: Annotated[\n Literal[\"structures\"],\n StrictField(\n description=\"\"\"The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`\"\"\",\n pattern=\"^structures$\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ] = \"structures\"\n\n attributes: StructureResourceAttributes\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.attributes","title":"attributes: StructureResourceAttributes
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.id","title":"id: Annotated[str, OptimadeField(description='An entry\\'s ID as defined in section Definition of Terms.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n\\n- **Examples**:\\n - `\"db/1234567\"`\\n - `\"cod/2000000\"`\\n - `\"cod/2000000@1234567\"`\\n - `\"nomad/L1234567890\"`\\n - `\"42\"`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.links","title":"links: Annotated[Optional[ResourceLinks], StrictField(description='a links object containing links related to the resource.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.meta","title":"meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.model_config","title":"model_config = ConfigDict(json_schema_extra=resource_json_schema_extra)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.relationships","title":"relationships: Annotated[Optional[EntryRelationships], StrictField(description='A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResource.type","title":"type: Annotated[Literal['structures'], StrictField(description='The name of the type of an entry.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response.\\n - MUST be an existing entry type.\\n - The entry of type `<type>` and ID `<id>` MUST be returned in response to a request for `/<type>/<id>` under the versioned base URL.\\n\\n- **Examples**:\\n - `\"structures\"`', pattern='^structures$', support=SupportLevel.MUST, queryable=SupportLevel.MUST)] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes","title":"StructureResourceAttributes
","text":" Bases: EntryResourceAttributes
This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.
Source code inoptimade/models/structures.py
class StructureResourceAttributes(EntryResourceAttributes):\n \"\"\"This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.\"\"\"\n\n elements: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n nelements: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n elements_ratios: Annotated[\n Optional[list[float]],\n OptimadeField(\n description=\"\"\"Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_descriptive: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n chemical_formula_reduced: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_hill: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n chemical_formula_anonymous: Annotated[\n Optional[str],\n OptimadeField(\n description=\"\"\"The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n pattern=CHEMICAL_FORMULA_REGEXP,\n ),\n ] = None\n\n dimension_types: Annotated[\n Optional[list[Periodicity]],\n OptimadeField(\n min_length=3,\n max_length=3,\n title=\"Dimension Types\",\n description=\"\"\"List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nperiodic_dimensions: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.MUST,\n ),\n ] = None\n\n lattice_vectors: Annotated[\n Optional[list[Vector3D_unknown]],\n OptimadeField(\n min_length=3,\n max_length=3,\n description=\"\"\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n cartesian_site_positions: Annotated[\n Optional[list[Vector3D]],\n OptimadeField(\n description=\"\"\"Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.\"\"\",\n unit=\"\u00c5\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n nsites: Annotated[\n Optional[int],\n OptimadeField(\n description=\"\"\"An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`\"\"\",\n queryable=SupportLevel.MUST,\n support=SupportLevel.SHOULD,\n ),\n ] = None\n\n species: Annotated[\n Optional[list[Species]],\n OptimadeField(\n description=\"\"\"A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n species_at_sites: Annotated[\n Optional[list[str]],\n OptimadeField(\n description=\"\"\"Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.\"\"\",\n support=SupportLevel.SHOULD,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n assemblies: Annotated[\n Optional[list[Assembly]],\n OptimadeField(\n description=\"\"\"A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).\"\"\",\n support=SupportLevel.OPTIONAL,\n queryable=SupportLevel.OPTIONAL,\n ),\n ] = None\n\n structure_features: Annotated[\n list[StructureFeatures],\n OptimadeField(\n title=\"Structure Features\",\n description=\"\"\"A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`\"\"\",\n support=SupportLevel.MUST,\n queryable=SupportLevel.MUST,\n ),\n ]\n\n @model_validator(mode=\"after\")\n def warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n\n @field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n @classmethod\n def check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n\n @field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n @classmethod\n def check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n\n @field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n )\n @classmethod\n def check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n ) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n\n @field_validator(\"elements\", mode=\"after\")\n @classmethod\n def elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n ) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n\n @field_validator(\"elements_ratios\", mode=\"after\")\n @classmethod\n def ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n ) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n\n @field_validator(\"lattice_vectors\", mode=\"after\")\n @classmethod\n def null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n ) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n\n @model_validator(mode=\"after\")\n def validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n\n @model_validator(mode=\"after\")\n def validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n\n @field_validator(\"species\", mode=\"after\")\n @classmethod\n def validate_species(\n cls, value: Optional[list[Species]]\n ) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n\n @model_validator(mode=\"after\")\n def validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.assemblies","title":"assemblies: Annotated[Optional[list[Assembly]], OptimadeField(description='A description of groups of sites that are statistically correlated.\\n\\n- **Type**: list of dictionary with keys:\\n - `sites_in_groups`: list of list of integers (REQUIRED)\\n - `group_probabilities`: list of floats (REQUIRED)\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - The property SHOULD be `null` for entries that have no partial occupancies.\\n - If present, the correct flag MUST be set in the list `structure_features`.\\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\\n\\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\\n\\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\\n It SHOULD sum to one.\\n See below for examples of how to specify the probability of the occurrence of a vacancy.\\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\\n\\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\\n - A site MUST NOT appear in more than one group.\\n\\n- **Examples** (for each entry of the assemblies list):\\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\\n The second group is formed by the fourth site.\\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\\n\\n- **Notes**:\\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\\n\\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\\n\\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\\n\\n - Using a single species:\\n ```json\\n {\\n \"cartesian_site_positions\": [[0,0,0]],\\n \"species_at_sites\": [\"SiGe-vac\"],\\n \"species\": [\\n {\\n \"name\": \"SiGe-vac\",\\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\\n \"concentration\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - Using multiple species and the assemblies:\\n ```json\\n {\\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\\n \"species\": [\\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\\n ],\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1], [2] ],\\n \"group_probabilities\": [0.3, 0.5, 0.2]\\n }\\n ]\\n // ...\\n }\\n ```\\n\\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\\n\\n - The probabilities of occurrence of different assemblies are uncorrelated.\\n So, for instance in the following case with two assemblies:\\n ```json\\n {\\n \"assemblies\": [\\n {\\n \"sites_in_groups\": [ [0], [1] ],\\n \"group_probabilities\": [0.2, 0.8],\\n },\\n {\\n \"sites_in_groups\": [ [2], [3] ],\\n \"group_probabilities\": [0.3, 0.7]\\n }\\n ]\\n }\\n ```\\n\\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\\n These two sites are correlated (either site 2 or 3 is present).\\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.cartesian_site_positions","title":"cartesian_site_positions: Annotated[Optional[list[Vector3D]], OptimadeField(description='Cartesian positions of each site in the structure.\\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\\n\\n- **Type**: list of list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\\n\\n- **Examples**:\\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.', unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_anonymous","title":"chemical_formula_anonymous: Annotated[Optional[str], OptimadeField(description='The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n\\n- **Examples**:\\n - `\"A2B\"`\\n - `\"A42B42C16D12E10F9G5\"`\\n\\n- **Querying**:\\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_descriptive","title":"chemical_formula_descriptive: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure as a string in a form chosen by the API implementation.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC\\'s Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\\n\\n- **Examples**:\\n - `\"(H2O)2 Na\"`\\n - `\"NaCl\"`\\n - `\"CaCO3\"`\\n - `\"CCaO3\"`\\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\\n\\n- **Query examples**:\\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_hill","title":"chemical_formula_hill: Annotated[Optional[str], OptimadeField(description='The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, only a subset of the filter features MAY be supported.\\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\\n After that, all other elements are ordered alphabetically.\\n If carbon is not present, all elements are ordered alphabetically.\\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2O2\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.', support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.chemical_formula_reduced","title":"chemical_formula_reduced: Annotated[Optional[str], OptimadeField(description='The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\\nThe proportion number MUST be omitted if it is 1.\\n\\n- **Type**: string\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\\n - No spaces or separators are allowed.\\n\\n- **Examples**:\\n - `\"H2NaO\"`\\n - `\"ClNa\"`\\n - `\"CCaO3\"`\\n\\n- **Query examples**:\\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST, pattern=CHEMICAL_FORMULA_REGEXP)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.dimension_types","title":"dimension_types: Annotated[Optional[list[Periodicity]], OptimadeField(min_length=3, max_length=3, title='Dimension Types', description='List of three integers.\\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\\n\\n- **Type**: list of integers.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n - MUST be a list of length 3.\\n - Each integer element MUST assume only the value 0 or 1.\\n\\n- **Examples**:\\n - For a molecule: `[0, 0, 0]`\\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\\n - For a bulk 3D system: `[1, 1, 1]`', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements","title":"elements: Annotated[Optional[list[str]], OptimadeField(description='The chemical symbols of the different elements present in the structure.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\\n - The order MUST be alphabetical.\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\\n\\n- **Examples**:\\n - `[\"Si\"]`\\n - `[\"Al\",\"O\",\"Si\"]`\\n\\n- **Query examples**:\\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements_ratios","title":"elements_ratios: Annotated[Optional[list[float]], OptimadeField(description='Relative proportions of different elements in the structure.\\n\\n- **Type**: list of floats\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\\n\\n- **Examples**:\\n - `[1.0]`\\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\\n\\n- **Query examples**:\\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.immutable_id","title":"immutable_id: Annotated[Optional[str], OptimadeField(description='The entry\\'s immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\\n\\n- **Type**: string.\\n\\n- **Requirements/Conventions**:\\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)', support=SupportLevel.OPTIONAL, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.last_modified","title":"last_modified: Annotated[Optional[datetime], OptimadeField(description='Date and time representing when the entry was last modified.\\n\\n- **Type**: timestamp.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\\n\\n- **Example**:\\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.lattice_vectors","title":"lattice_vectors: Annotated[Optional[list[Vector3D_unknown]], OptimadeField(min_length=3, max_length=3, description=\"The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\\n\\n- **Type**: list of list of floats or unknown values.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\\n\\n- **Examples**:\\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.\", unit=\u00c5, support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.model_config","title":"model_config = ConfigDict(extra='allow')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nelements","title":"nelements: Annotated[Optional[int], OptimadeField(description='Number of different elements in the structure as an integer.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\\n\\n- **Examples**:\\n - `3`\\n\\n- **Querying**:\\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nperiodic_dimensions","title":"nperiodic_dimensions: Annotated[Optional[int], OptimadeField(description='An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\\n\\n- **Examples**:\\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\\n\\n- **Query examples**:\\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`', support=SupportLevel.SHOULD, queryable=SupportLevel.MUST)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.nsites","title":"nsites: Annotated[Optional[int], OptimadeField(description='An integer specifying the length of the `cartesian_site_positions` property.\\n\\n- **Type**: integer\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\\n\\n- **Examples**:\\n - `42`\\n\\n- **Query examples**:\\n - Match only structures with exactly 4 sites: `nsites=4`\\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`', queryable=SupportLevel.MUST, support=SupportLevel.SHOULD)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.species","title":"species: Annotated[Optional[list[Species]], OptimadeField(description='A list describing the species of the sites of this structure.\\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\\n\\n- **Type**: list of dictionary with keys:\\n - `name`: string (REQUIRED)\\n - `chemical_symbols`: list of strings (REQUIRED)\\n - `concentration`: list of float (REQUIRED)\\n - `attached`: list of strings (REQUIRED)\\n - `nattached`: list of integers (OPTIONAL)\\n - `mass`: list of floats (OPTIONAL)\\n - `original_name`: string (OPTIONAL).\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - Each list member MUST be a dictionary with the following keys:\\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\\n Each item of the list MUST be one of the following:\\n - a valid chemical-element symbol, or\\n - the special value `\"X\"` to represent a non-chemical element, or\\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\\n\\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\\n\\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\\n\\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\\n\\n Note that concentrations are uncorrelated between different sites (even of the same species).\\n\\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\\n\\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\\n\\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\\n\\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\\n Elements denoting vacancies MUST have masses equal to 0.\\n\\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\\n\\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\\n\\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\\n\\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\\n\\n- **Examples**:\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.species_at_sites","title":"species_at_sites: Annotated[Optional[list[str]], OptimadeField(description='Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\\nThe properties of the species are found in the property `species`.\\n\\n- **Type**: list of strings.\\n\\n- **Requirements/Conventions**:\\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\\n - **Query**: Support for queries on this property is OPTIONAL.\\n If supported, filters MAY support only a subset of comparison operators.\\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\\n - Each site MUST be associated only to a single species.\\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\\n\\n- **Examples**:\\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.', support=SupportLevel.SHOULD, queryable=SupportLevel.OPTIONAL)] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.structure_features","title":"structure_features: Annotated[list[StructureFeatures], OptimadeField(title='Structure Features', description='A list of strings that flag which special features are used by the structure.\\n\\n- **Type**: list of strings\\n\\n- **Requirements/Conventions**:\\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\\n - **Query**: MUST be a queryable property.\\n Filters on the list MUST support all mandatory HAS-type queries.\\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\\n - MUST be an empty list if no special features are used.\\n - MUST be sorted alphabetically.\\n - If a special feature listed below is used, the list MUST contain the corresponding string.\\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\\n - **List of strings used to indicate special structure features**:\\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\\n\\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`', support=SupportLevel.MUST, queryable=SupportLevel.MUST)]
instance-attribute
","text":""},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.cast_immutable_id_to_str","title":"cast_immutable_id_to_str(value)
classmethod
","text":"Convenience validator for casting immutable_id
to a string.
optimade/models/entries.py
@field_validator(\"immutable_id\", mode=\"before\")\n@classmethod\ndef cast_immutable_id_to_str(cls, value: Any) -> str:\n \"\"\"Convenience validator for casting `immutable_id` to a string.\"\"\"\n if value is not None and not isinstance(value, str):\n value = str(value)\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_anonymous_formula","title":"check_anonymous_formula(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_anonymous\", mode=\"after\")\n@classmethod\ndef check_anonymous_formula(cls, value: Optional[str]) -> Optional[str]:\n if value is None:\n return value\n\n elements = tuple(re.findall(r\"[A-Z][a-z]*\", value))\n numbers = re.split(r\"[A-Z][a-z]*\", value)[1:]\n numbers = [int(i) if i else 1 for i in numbers]\n\n expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]\n expected_numbers = sorted(numbers, reverse=True)\n\n if expected_numbers != numbers:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong order: elements with \"\n f\"highest proportion should appear first: {numbers} vs expected \"\n f\"{expected_numbers}\"\n )\n if elements != expected_labels:\n raise ValueError(\n f\"'chemical_formula_anonymous' {value} has wrong labels: {elements} vs\"\n f\" expected {expected_labels}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_dimensions_types_dependencies","title":"check_dimensions_types_dependencies()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef check_dimensions_types_dependencies(self) -> \"StructureResourceAttributes\":\n if self.nperiodic_dimensions is not None:\n if self.dimension_types and self.nperiodic_dimensions != sum(\n self.dimension_types\n ):\n raise ValueError(\n f\"nperiodic_dimensions ({self.nperiodic_dimensions}) does not match \"\n f\"expected value of {sum(self.dimension_types)} from dimension_types \"\n f\"({self.dimension_types})\"\n )\n\n if self.lattice_vectors is not None:\n if self.dimension_types:\n for dim_type, vector in zip(self.dimension_types, self.lattice_vectors):\n if None in vector and dim_type == Periodicity.PERIODIC.value:\n raise ValueError(\n f\"Null entries in lattice vectors are only permitted when the \"\n \"corresponding dimension type is \"\n f\"{Periodicity.APERIODIC.value}. Here: dimension_types = \"\n f\"{tuple(getattr(_, 'value', None) for _ in self.dimension_types)},\"\n f\" lattice_vectors = {self.lattice_vectors}\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_illegal_attributes_fields","title":"check_illegal_attributes_fields()
","text":"Source code in optimade/models/jsonapi.py
@model_validator(mode=\"after\")\ndef check_illegal_attributes_fields(self) -> \"Attributes\":\n illegal_fields = (\"relationships\", \"links\", \"id\", \"type\")\n for field in illegal_fields:\n if hasattr(self, field):\n raise ValueError(\n f\"{illegal_fields} MUST NOT be fields under Attributes\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_ordered_formula","title":"check_ordered_formula(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"chemical_formula_reduced\", \"chemical_formula_hill\", mode=\"after\")\n@classmethod\ndef check_ordered_formula(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n elements = re.findall(r\"[A-Z][a-z]?\", value)\n expected_elements = sorted(elements)\n\n if info.field_name == \"chemical_formula_hill\":\n # Make sure C is first (and H is second, if present along with C).\n if \"C\" in expected_elements:\n expected_elements = sorted(\n expected_elements,\n key=lambda elem: {\"C\": \"0\", \"H\": \"1\"}.get(elem, elem),\n )\n\n if any(elem not in CHEMICAL_SYMBOLS for elem in elements):\n raise ValueError(\n f\"Cannot use unknown chemical symbols {[elem for elem in elements if elem not in CHEMICAL_SYMBOLS]} in {info.field_name!r}\"\n )\n\n if expected_elements != elements:\n order = (\n \"Hill\" if info.field_name == \"chemical_formula_hill\" else \"alphabetical\"\n )\n raise ValueError(\n f\"Elements in {info.field_name!r} must appear in {order} order: {expected_elements} not {elements}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.check_reduced_formulae","title":"check_reduced_formulae(value, info)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\n \"chemical_formula_anonymous\", \"chemical_formula_reduced\", mode=\"after\"\n)\n@classmethod\ndef check_reduced_formulae(\n cls, value: Optional[str], info: \"ValidationInfo\"\n) -> Optional[str]:\n if value is None:\n return value\n\n reduced_formula = reduce_formula(value)\n if reduced_formula != value:\n raise ValueError(\n f\"{info.field_name} {value!r} is not properly reduced: expected \"\n f\"{reduced_formula!r}.\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.elements_must_be_alphabetical","title":"elements_must_be_alphabetical(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements\", mode=\"after\")\n@classmethod\ndef elements_must_be_alphabetical(\n cls, value: Optional[list[str]]\n) -> Optional[list[str]]:\n if value is None:\n return value\n\n if sorted(value) != value:\n raise ValueError(f\"elements must be sorted alphabetically, but is: {value}\")\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.null_values_for_whole_vector","title":"null_values_for_whole_vector(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"lattice_vectors\", mode=\"after\")\n@classmethod\ndef null_values_for_whole_vector(\n cls,\n value: Optional[\n Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]\n ],\n) -> Optional[Annotated[list[Vector3D_unknown], Field(min_length=3, max_length=3)]]:\n if value is None:\n return value\n\n for vector in value:\n if None in vector and any(isinstance(_, float) for _ in vector):\n raise ValueError(\n \"A lattice vector MUST be either all `null` or all numbers \"\n f\"(vector: {vector}, all vectors: {value})\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.ratios_must_sum_to_one","title":"ratios_must_sum_to_one(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"elements_ratios\", mode=\"after\")\n@classmethod\ndef ratios_must_sum_to_one(\n cls, value: Optional[list[float]]\n) -> Optional[list[float]]:\n if value is None:\n return value\n\n if abs(sum(value) - 1) > EPS:\n raise ValueError(\n \"elements_ratios MUST sum to 1 within (at least single precision) \"\n f\"floating point accuracy. It sums to: {sum(value)}\"\n )\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_nsites","title":"validate_nsites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_nsites(self) -> \"StructureResourceAttributes\":\n if self.nsites is None:\n return self\n\n if self.cartesian_site_positions and self.nsites != len(\n self.cartesian_site_positions\n ):\n raise ValueError(\n f\"nsites (value: {self.nsites}) MUST equal length of \"\n \"cartesian_site_positions (value: \"\n f\"{len(self.cartesian_site_positions)})\"\n )\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_species","title":"validate_species(value)
classmethod
","text":"Source code in optimade/models/structures.py
@field_validator(\"species\", mode=\"after\")\n@classmethod\ndef validate_species(\n cls, value: Optional[list[Species]]\n) -> Optional[list[Species]]:\n if value is None:\n return value\n\n all_species = [_.name for _ in value]\n unique_species = set(all_species)\n if len(all_species) != len(unique_species):\n raise ValueError(\n f\"Species MUST be unique based on their 'name'. Found species names: {all_species}\"\n )\n\n return value\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_species_at_sites","title":"validate_species_at_sites()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_species_at_sites(self) -> \"StructureResourceAttributes\":\n if self.species_at_sites is None:\n return self\n\n if self.nsites and len(self.species_at_sites) != self.nsites:\n raise ValueError(\n f\"Number of species_at_sites (value: {len(self.species_at_sites)}) \"\n f\"MUST equal number of sites (value: {self.nsites})\"\n )\n\n if self.species:\n all_species_names = {_.name for _ in self.species}\n\n for species_at_site in self.species_at_sites:\n if species_at_site not in all_species_names:\n raise ValueError(\n \"species_at_sites MUST be represented by a species' name, \"\n f\"but {species_at_site} was not found in the list of species \"\n f\"names: {all_species_names}\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.validate_structure_features","title":"validate_structure_features()
","text":"Source code in optimade/models/structures.py
@model_validator(mode=\"after\")\ndef validate_structure_features(self) -> \"StructureResourceAttributes\":\n if [\n StructureFeatures(value)\n for value in sorted(_.value for _ in self.structure_features)\n ] != self.structure_features:\n raise ValueError(\n \"structure_features MUST be sorted alphabetically, structure_features: \"\n f\"{self.structure_features}\"\n )\n\n # assemblies\n if self.assemblies is not None:\n if StructureFeatures.ASSEMBLIES not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST be present, since the \"\n \"property of the same name is present\"\n )\n elif StructureFeatures.ASSEMBLIES in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.ASSEMBLIES.value} MUST NOT be present, \"\n \"since the property of the same name is not present\"\n )\n\n if self.species:\n # disorder\n for species in self.species:\n if len(species.chemical_symbols) > 1:\n if StructureFeatures.DISORDER not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST be present when \"\n \"any one entry in species has a chemical_symbols list \"\n \"greater than one element\"\n )\n break\n else:\n if StructureFeatures.DISORDER in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.DISORDER.value} MUST NOT be present, \"\n \"since all species' chemical_symbols lists are equal to or \"\n \"less than one element\"\n )\n\n # site_attachments\n for species in self.species:\n # There is no need to also test \"nattached\",\n # since a Species validator makes sure either both are present or both are None.\n if species.attached is not None:\n if (\n StructureFeatures.SITE_ATTACHMENTS\n not in self.structure_features\n ):\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST be \"\n \"present when any one entry in species includes attached \"\n \"and nattached\"\n )\n break\n else:\n if StructureFeatures.SITE_ATTACHMENTS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.SITE_ATTACHMENTS.value} MUST NOT be \"\n \"present, since no species includes the attached and nattached\"\n \" fields\"\n )\n\n # implicit_atoms\n for name in [_.name for _ in self.species]:\n if (\n self.species_at_sites is not None\n and name not in self.species_at_sites\n ):\n if StructureFeatures.IMPLICIT_ATOMS not in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST be present\"\n \" when any one entry in species is not represented in \"\n \"species_at_sites\"\n )\n break\n else:\n if StructureFeatures.IMPLICIT_ATOMS in self.structure_features:\n raise ValueError(\n f\"{StructureFeatures.IMPLICIT_ATOMS.value} MUST NOT be \"\n \"present, since all species are represented in species_at_sites\"\n )\n\n return self\n
"},{"location":"api_reference/models/structures/#optimade.models.structures.StructureResourceAttributes.warn_on_missing_correlated_fields","title":"warn_on_missing_correlated_fields()
","text":"Emit warnings if a field takes a null value when a value was expected based on the value/nullity of another field.
Source code inoptimade/models/structures.py
@model_validator(mode=\"after\")\ndef warn_on_missing_correlated_fields(self) -> \"StructureResourceAttributes\":\n \"\"\"Emit warnings if a field takes a null value when a value\n was expected based on the value/nullity of another field.\n \"\"\"\n accumulated_warnings = []\n for field_set in CORRELATED_STRUCTURE_FIELDS:\n missing_fields = {\n field for field in field_set if getattr(self, field, None) is None\n }\n if missing_fields and len(missing_fields) != len(field_set):\n accumulated_warnings += [\n f\"Structure with attributes {self} is missing fields \"\n f\"{missing_fields} which are required if \"\n f\"{field_set - missing_fields} are present.\"\n ]\n\n for warn in accumulated_warnings:\n warnings.warn(warn, MissingExpectedField)\n\n return self\n
"},{"location":"api_reference/models/types/","title":"types","text":""},{"location":"api_reference/models/types/#optimade.models.types.AnnotatedType","title":"AnnotatedType = type(ChemicalSymbol)
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.ChemicalSymbol","title":"ChemicalSymbol = Annotated[str, Field(pattern=EXTENDED_CHEMICAL_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.ElementSymbol","title":"ElementSymbol = Annotated[str, Field(pattern=ELEMENT_SYMBOLS_PATTERN)]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.NoneType","title":"NoneType = type(None)
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.OptionalType","title":"OptionalType = type(Optional[str])
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.SemanticVersion","title":"SemanticVersion = Annotated[str, Field(pattern=SEMVER_PATTERN, examples=['0.10.1', '1.0.0-rc.2', '1.2.3-rc.5+develop'])]
module-attribute
","text":""},{"location":"api_reference/models/types/#optimade.models.types.UnionType","title":"UnionType = type(Union[str, int])
module-attribute
","text":""},{"location":"api_reference/models/utils/","title":"utils","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.ANONYMOUS_ELEMENTS","title":"ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150))
module-attribute
","text":"Returns the first 150 values of the anonymous element generator.
"},{"location":"api_reference/models/utils/#optimade.models.utils.ATOMIC_NUMBERS","title":"ATOMIC_NUMBERS = {}
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.CHEMICAL_FORMULA_REGEXP","title":"CHEMICAL_FORMULA_REGEXP = '(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\\\d+)?)+$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.CHEMICAL_SYMBOLS","title":"CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.ELEMENT_SYMBOLS_PATTERN","title":"ELEMENT_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.EXTENDED_CHEMICAL_SYMBOLS_PATTERN","title":"EXTENDED_CHEMICAL_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS + EXTRA_SYMBOLS) + ')'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.EXTRA_SYMBOLS","title":"EXTRA_SYMBOLS = ['X', 'vacancy']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.IDENTIFIER_REGEX","title":"IDENTIFIER_REGEX = '^[a-z_][a-z_0-9]+$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_KEYS","title":"OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable']
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OPTIMADE_SCHEMA_EXTENSION_PREFIX","title":"OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SEMVER_PATTERN","title":"SEMVER_PATTERN = '^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$'
module-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel","title":"SupportLevel
","text":" Bases: Enum
OPTIMADE property/field support levels
Source code inoptimade/models/utils.py
class SupportLevel(Enum):\n \"\"\"OPTIMADE property/field support levels\"\"\"\n\n MUST = \"must\"\n SHOULD = \"should\"\n OPTIONAL = \"optional\"\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.MUST","title":"MUST = 'must'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.OPTIONAL","title":"OPTIONAL = 'optional'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.SupportLevel.SHOULD","title":"SHOULD = 'should'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/models/utils/#optimade.models.utils.OptimadeField","title":"OptimadeField(default=PydanticUndefined, *, support=None, queryable=None, unit=None, **kwargs)
","text":"A wrapper around pydantic.Field
that adds OPTIMADE-specific field paramters queryable
, support
and unit
, indicating the corresponding support level in the specification and the physical unit of the field.
Parameters:
Name Type Description Defaultsupport
Optional[Union[str, SupportLevel]]
The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.
None
queryable
Optional[Union[str, SupportLevel]]
The support level corresponding to the queryablility of this field.
None
unit
Optional[str]
A string describing the unit of the field.
None
Returns:
Type DescriptionAny
The pydantic field with extra validation provided by StrictField
.
optimade/models/utils.py
def OptimadeField(\n default: \"Any\" = PydanticUndefined,\n *,\n support: Optional[Union[str, SupportLevel]] = None,\n queryable: Optional[Union[str, SupportLevel]] = None,\n unit: Optional[str] = None,\n **kwargs,\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that adds OPTIMADE-specific\n field paramters `queryable`, `support` and `unit`, indicating\n the corresponding support level in the specification and the\n physical unit of the field.\n\n Arguments:\n support: The support level of the field itself, i.e. whether the field\n can be null or omitted by an implementation.\n queryable: The support level corresponding to the queryablility\n of this field.\n unit: A string describing the unit of the field.\n\n Returns:\n The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].\n\n \"\"\"\n\n # Collect non-null keyword arguments to add to the Field schema\n if unit is not None:\n kwargs[\"unit\"] = unit\n\n if queryable is not None:\n if isinstance(queryable, str):\n queryable = SupportLevel(queryable.lower())\n kwargs[\"queryable\"] = queryable\n\n if support is not None:\n if isinstance(support, str):\n support = SupportLevel(support.lower())\n kwargs[\"support\"] = support\n\n return StrictField(default, **kwargs)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.StrictField","title":"StrictField(default=PydanticUndefined, *, description=None, **kwargs)
","text":"A wrapper around pydantic.Field
that does the following:
pydantic.Field
, except those used elsewhere to modify the schema in-place, e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g. \"unit\", \"queryable\" and \"sortable\".Parameters:
Name Type Description Defaultdefault
Any
The only non-keyword argument allowed for Field.
PydanticUndefined
description
Optional[str]
The description of the Field
; if this is not specified then a UserWarning
will be emitted.
None
**kwargs
Any
Extra keyword arguments to be passed to Field
.
{}
Raises:
Type DescriptionRuntimeError
If **kwargs
contains a key not found in the function signature of Field
, or in the extensions used by models in this package (see above).
Returns:
Type DescriptionAny
The pydantic Field
.
optimade/models/utils.py
def StrictField(\n default: \"Any\" = PydanticUndefined,\n *,\n description: Optional[str] = None,\n **kwargs: \"Any\",\n) -> Any:\n \"\"\"A wrapper around `pydantic.Field` that does the following:\n\n - Forbids any \"extra\" keys that would be passed to `pydantic.Field`,\n except those used elsewhere to modify the schema in-place,\n e.g. \"uniqueItems\", \"pattern\" and those added by OptimadeField, e.g.\n \"unit\", \"queryable\" and \"sortable\".\n - Emits a warning when no description is provided.\n\n Arguments:\n default: The only non-keyword argument allowed for Field.\n description: The description of the `Field`; if this is not\n specified then a `UserWarning` will be emitted.\n **kwargs: Extra keyword arguments to be passed to `Field`.\n\n Raises:\n RuntimeError: If `**kwargs` contains a key not found in the\n function signature of `Field`, or in the extensions used\n by models in this package (see above).\n\n Returns:\n The pydantic `Field`.\n\n \"\"\"\n allowed_schema_and_field_keys = [\"pattern\"]\n\n allowed_keys = [\n \"pattern\",\n \"uniqueItems\",\n ] + OPTIMADE_SCHEMA_EXTENSION_KEYS\n _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]\n\n if _banned:\n raise RuntimeError(\n f\"Not creating StrictField({default!r}, **{kwargs!r}) with \"\n f\"forbidden keywords {_banned}.\"\n )\n\n # Handle description\n if description is None:\n warnings.warn(\n f\"No description provided for StrictField specified by {default!r}, \"\n f\"**{kwargs!r}.\"\n )\n else:\n kwargs[\"description\"] = description\n\n # OPTIMADE schema extensions\n json_schema_extra: dict[str, Any] = kwargs.pop(\"json_schema_extra\", {})\n\n # Go through all JSON Schema keys and add them to the json_schema_extra.\n for key in allowed_keys:\n if key not in kwargs:\n continue\n\n # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.\n schema_key = (\n f\"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}\"\n if key in OPTIMADE_SCHEMA_EXTENSION_KEYS\n else key\n )\n\n for key_variant in (key, schema_key):\n if key_variant in json_schema_extra:\n if json_schema_extra.pop(key_variant) != kwargs[key]:\n raise RuntimeError(\n f\"Conflicting values for {key} in json_schema_extra and kwargs.\"\n )\n\n json_schema_extra[schema_key] = (\n kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)\n )\n\n kwargs[\"json_schema_extra\"] = json_schema_extra\n\n return Field(default, **kwargs)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.anonymize_formula","title":"anonymize_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and returns the OPTIMADE chemical_formula_anonymous
representation, i.e., a reduced chemical formula comprising of element symbols drawn from A, B, C... ordered from largest proportion to smallest.
Returns:
Type Descriptionstr
The anonymous chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def anonymize_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n returns the OPTIMADE `chemical_formula_anonymous` representation, i.e., a reduced chemical formula comprising of element symbols\n drawn from A, B, C... ordered from largest proportion to smallest.\n\n Returns:\n The anonymous chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=False, anonymize=True)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.anonymous_element_generator","title":"anonymous_element_generator()
","text":"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.
Source code inoptimade/models/utils.py
def anonymous_element_generator() -> \"Generator[str, None, None]\":\n \"\"\"Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.\"\"\"\n from string import ascii_lowercase\n\n for size in itertools.count(1):\n for tuple_strings in itertools.product(ascii_lowercase, repeat=size):\n list_strings = list(tuple_strings)\n list_strings[0] = list_strings[0].upper()\n yield \"\".join(list_strings)\n
"},{"location":"api_reference/models/utils/#optimade.models.utils.reduce_formula","title":"reduce_formula(formula)
","text":"Takes a string representation of a chemical formula of the form [A-Z][a-z]*[0-9]*
(potentially with whitespace) and reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.
Returns:
Type Descriptionstr
The reduced chemical formula in the OPTIMADE representation.
Source code inoptimade/models/utils.py
def reduce_formula(formula: str) -> str:\n \"\"\"Takes a string representation of a chemical formula of the form `[A-Z][a-z]*[0-9]*` (potentially with whitespace) and\n reduces it by the GCD of the proportion integers present in the formula, stripping any leftover \"1\" values.\n\n Returns:\n The reduced chemical formula in the OPTIMADE representation.\n\n \"\"\"\n return _reduce_or_anonymize_formula(formula, alphabetize=True, anonymize=False)\n
"},{"location":"api_reference/server/config/","title":"config","text":""},{"location":"api_reference/server/config/#optimade.server.config.CONFIG","title":"CONFIG: ServerConfig = ServerConfig()
module-attribute
","text":"This singleton loads the config from a hierarchy of sources (see customise_sources
) and makes it importable in the server code.
DEFAULT_CONFIG_FILE_PATH: str = str(Path.home().joinpath('.optimade.json'))
module-attribute
","text":"Default configuration file path.
This variable is used as the fallback value if the environment variable OPTIMADE_CONFIG_FILE
is not set.
Note
It is set to: pathlib.Path.home()/.optimade.json
For Unix-based systems (Linux) this will be equivalent to ~/.optimade.json
.
ConfigFileSettingsSource
","text":" Bases: PydanticBaseSettingsSource
Configuration file settings source.
Based on the example in the pydantic documentation, this class defines loading ServerConfig settings from a configuration file.
The file must be of either type JSON or YML/YAML.
Source code inoptimade/server/config.py
class ConfigFileSettingsSource(PydanticBaseSettingsSource):\n \"\"\"Configuration file settings source.\n\n Based on the example in the\n [pydantic documentation](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#customise-settings-sources),\n this class defines loading ServerConfig settings from a configuration file.\n\n The file must be of either type JSON or YML/YAML.\n \"\"\"\n\n def get_field_value(\n self, field: FieldInfo, field_name: str\n ) -> tuple[Any, str, bool]:\n \"\"\"Must be defined according to parent abstract class.\n\n It does not make sense to use it for this class, since 'extra' is set to\n 'allow'. We will instead just parse and take every key/field specified in the\n config file.\n \"\"\"\n raise NotImplementedError\n\n def parse_config_file(self) -> dict[str, Any]:\n \"\"\"Parse the config file and return a dictionary of its content.\"\"\"\n encoding = self.config.get(\"env_file_encoding\")\n config_file = Path(os.getenv(\"OPTIMADE_CONFIG_FILE\", DEFAULT_CONFIG_FILE_PATH))\n\n parsed_config_file = {}\n if config_file.is_file():\n config_file_content = config_file.read_text(encoding=encoding)\n\n try:\n parsed_config_file = json.loads(config_file_content)\n except json.JSONDecodeError as json_exc:\n try:\n # This can essentially also load JSON files, as JSON is a subset of\n # YAML v1, but I suspect it is not as rigorous\n parsed_config_file = yaml.safe_load(config_file_content)\n except yaml.YAMLError as yaml_exc:\n warnings.warn(\n f\"Unable to parse config file {config_file} as JSON or \"\n \"YAML, using the default settings instead..\\n\"\n f\"Errors:\\n JSON:\\n{json_exc}.\\n\\n YAML:\\n{yaml_exc}\"\n )\n else:\n warnings.warn(\n f\"Unable to find config file at {config_file}, using the default \"\n \"settings instead.\"\n )\n\n if parsed_config_file is None:\n # This can happen if the yaml loading doesn't succeed properly, e.g., if\n # the file is empty.\n warnings.warn(\n f\"Unable to load any settings from {config_file}, using the default \"\n \"settings instead.\"\n )\n parsed_config_file = {}\n\n if not isinstance(parsed_config_file, dict):\n warnings.warn(\n f\"Unable to parse config file {config_file} as a dictionary, using \"\n \"the default settings instead.\"\n )\n parsed_config_file = {}\n\n return parsed_config_file\n\n def __call__(self) -> dict[str, Any]:\n return self.parse_config_file()\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.__call__","title":"__call__()
","text":"Source code in optimade/server/config.py
def __call__(self) -> dict[str, Any]:\n return self.parse_config_file()\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.get_field_value","title":"get_field_value(field, field_name)
","text":"Must be defined according to parent abstract class.
It does not make sense to use it for this class, since 'extra' is set to 'allow'. We will instead just parse and take every key/field specified in the config file.
Source code inoptimade/server/config.py
def get_field_value(\n self, field: FieldInfo, field_name: str\n) -> tuple[Any, str, bool]:\n \"\"\"Must be defined according to parent abstract class.\n\n It does not make sense to use it for this class, since 'extra' is set to\n 'allow'. We will instead just parse and take every key/field specified in the\n config file.\n \"\"\"\n raise NotImplementedError\n
"},{"location":"api_reference/server/config/#optimade.server.config.ConfigFileSettingsSource.parse_config_file","title":"parse_config_file()
","text":"Parse the config file and return a dictionary of its content.
Source code inoptimade/server/config.py
def parse_config_file(self) -> dict[str, Any]:\n \"\"\"Parse the config file and return a dictionary of its content.\"\"\"\n encoding = self.config.get(\"env_file_encoding\")\n config_file = Path(os.getenv(\"OPTIMADE_CONFIG_FILE\", DEFAULT_CONFIG_FILE_PATH))\n\n parsed_config_file = {}\n if config_file.is_file():\n config_file_content = config_file.read_text(encoding=encoding)\n\n try:\n parsed_config_file = json.loads(config_file_content)\n except json.JSONDecodeError as json_exc:\n try:\n # This can essentially also load JSON files, as JSON is a subset of\n # YAML v1, but I suspect it is not as rigorous\n parsed_config_file = yaml.safe_load(config_file_content)\n except yaml.YAMLError as yaml_exc:\n warnings.warn(\n f\"Unable to parse config file {config_file} as JSON or \"\n \"YAML, using the default settings instead..\\n\"\n f\"Errors:\\n JSON:\\n{json_exc}.\\n\\n YAML:\\n{yaml_exc}\"\n )\n else:\n warnings.warn(\n f\"Unable to find config file at {config_file}, using the default \"\n \"settings instead.\"\n )\n\n if parsed_config_file is None:\n # This can happen if the yaml loading doesn't succeed properly, e.g., if\n # the file is empty.\n warnings.warn(\n f\"Unable to load any settings from {config_file}, using the default \"\n \"settings instead.\"\n )\n parsed_config_file = {}\n\n if not isinstance(parsed_config_file, dict):\n warnings.warn(\n f\"Unable to parse config file {config_file} as a dictionary, using \"\n \"the default settings instead.\"\n )\n parsed_config_file = {}\n\n return parsed_config_file\n
"},{"location":"api_reference/server/config/#optimade.server.config.LogLevel","title":"LogLevel
","text":" Bases: Enum
Replication of logging LogLevels
notset
debug
info
warning
error
critical
optimade/server/config.py
class LogLevel(Enum):\n \"\"\"Replication of logging LogLevels\n\n - `notset`\n - `debug`\n - `info`\n - `warning`\n - `error`\n - `critical`\n\n \"\"\"\n\n NOTSET = \"notset\"\n DEBUG = \"debug\"\n INFO = \"info\"\n WARNING = \"warning\"\n ERROR = \"error\"\n CRITICAL = \"critical\"\n
"},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.CRITICAL","title":"CRITICAL = 'critical'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.DEBUG","title":"DEBUG = 'debug'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.ERROR","title":"ERROR = 'error'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.INFO","title":"INFO = 'info'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.NOTSET","title":"NOTSET = 'notset'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.LogLevel.WARNING","title":"WARNING = 'warning'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig","title":"ServerConfig
","text":" Bases: BaseSettings
This class stores server config parameters in a way that can be easily extended for new config file types.
Source code inoptimade/server/config.py
class ServerConfig(BaseSettings):\n \"\"\"This class stores server config parameters in a way that\n can be easily extended for new config file types.\n \"\"\"\n\n model_config = SettingsConfigDict(\n env_prefix=\"optimade_\",\n extra=\"allow\",\n env_file_encoding=\"utf-8\",\n case_sensitive=False,\n )\n\n debug: Annotated[\n bool,\n Field(\n description=\"Turns on Debug Mode for the OPTIMADE Server implementation\",\n ),\n ] = False\n\n insert_test_data: Annotated[\n bool,\n Field(\n description=(\n \"Insert test data into each collection on server initialisation. If true, \"\n \"the configured backend will be populated with test data on server start. \"\n \"Should be disabled for production usage.\"\n ),\n ),\n ] = True\n\n insert_from_jsonl: Annotated[\n Optional[Path],\n Field(\n description=(\n \"The absolute path to an OPTIMADE JSONL file to use to initialize the database. \"\n \"A unique index will be created over the ID to avoid duplication over multiple runs.\"\n )\n ),\n ] = None\n\n use_real_mongo: Annotated[\n Optional[bool],\n Field(description=\"DEPRECATED: force usage of MongoDB over any other backend.\"),\n ] = None\n\n database_backend: Annotated[\n SupportedBackend,\n Field(\n description=\"Which database backend to use out of the supported backends.\",\n ),\n ] = SupportedBackend.MONGOMOCK\n\n elastic_hosts: Annotated[\n Optional[Union[str, list[str], dict[str, Any], list[dict[str, Any]]]],\n Field(\n description=\"Host settings to pass through to the `Elasticsearch` class.\"\n ),\n ] = None\n\n mongo_count_timeout: Annotated[\n int,\n Field(\n description=(\n \"Number of seconds to allow MongoDB to perform a full database count \"\n \"before falling back to `null`. This operation can require a full COLLSCAN\"\n \" for empty queries which can be prohibitively slow if the database does \"\n \"not fit into the active set, hence a timeout can drastically speed-up \"\n \"response times.\"\n ),\n ),\n ] = 5\n\n mongo_database: Annotated[\n str, Field(description=\"Mongo database for collection data\")\n ] = \"optimade\"\n mongo_uri: Annotated[str, Field(description=\"URI for the Mongo server\")] = (\n \"localhost:27017\"\n )\n links_collection: Annotated[\n str, Field(description=\"Mongo collection name for /links endpoint resources\")\n ] = \"links\"\n references_collection: Annotated[\n str,\n Field(\n description=\"Mongo collection name for /references endpoint resources\",\n ),\n ] = \"references\"\n structures_collection: Annotated[\n str,\n Field(\n description=\"Mongo collection name for /structures endpoint resources\",\n ),\n ] = \"structures\"\n page_limit: Annotated[\n int, Field(description=\"Default number of resources per page\")\n ] = 20\n page_limit_max: Annotated[\n int, Field(description=\"Max allowed number of resources per page\")\n ] = 500\n default_db: Annotated[\n str,\n Field(\n description=(\n \"ID of /links endpoint resource for the chosen default OPTIMADE \"\n \"implementation (only relevant for the index meta-database)\"\n ),\n ),\n ] = \"test_server\"\n root_path: Annotated[\n Optional[str],\n Field(\n description=(\n \"Sets the FastAPI app `root_path` parameter. This can be used to serve the\"\n \" API under a path prefix behind a proxy or as a sub-application of \"\n \"another FastAPI app. See \"\n \"https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path\"\n \" for details.\"\n ),\n ),\n ] = None\n base_url: Annotated[\n Optional[str], Field(description=\"Base URL for this implementation\")\n ] = None\n implementation: Annotated[\n Implementation,\n Field(\n description=\"Introspective information about this OPTIMADE implementation\",\n ),\n ] = Implementation(\n name=\"OPTIMADE Python Tools\",\n version=__version__,\n source_url=\"https://github.com/Materials-Consortia/optimade-python-tools\",\n maintainer={\"email\": \"dev@optimade.org\"},\n issue_tracker=(\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues\"\n ),\n homepage=\"https://optimade.org/optimade-python-tools\",\n )\n index_base_url: Annotated[\n Optional[AnyHttpUrl],\n Field(\n description=(\n \"An optional link to the base URL for the index meta-database of the \"\n \"provider.\"\n ),\n ),\n ] = None\n provider: Annotated[\n Provider,\n Field(\n description=(\n \"General information about the provider of this OPTIMADE implementation\"\n ),\n ),\n ] = Provider(\n prefix=\"exmpl\",\n name=\"Example provider\",\n description=(\n \"Provider used for examples, not to be assigned to a real database\"\n ),\n homepage=\"https://example.com\",\n )\n provider_fields: Annotated[\n dict[\n Literal[\"links\", \"references\", \"structures\"],\n list[Union[str, dict[Literal[\"name\", \"type\", \"unit\", \"description\"], str]]],\n ],\n Field(\n description=(\n \"A list of additional fields to be served with the provider's prefix \"\n \"attached, broken down by endpoint.\"\n ),\n ),\n ] = {}\n aliases: Annotated[\n dict[Literal[\"links\", \"references\", \"structures\"], dict[str, str]],\n Field(\n description=(\n \"A mapping between field names in the database with their corresponding \"\n \"OPTIMADE field names, broken down by endpoint.\"\n ),\n ),\n ] = {}\n length_aliases: Annotated[\n dict[Literal[\"links\", \"references\", \"structures\"], dict[str, str]],\n Field(\n description=(\n \"A mapping between a list property (or otherwise) and an integer property \"\n \"that defines the length of that list, for example elements -> nelements. \"\n \"The standard aliases are applied first, so this dictionary must refer to \"\n \"the API fields, not the database fields.\"\n ),\n ),\n ] = {}\n index_links_path: Annotated[\n Path,\n Field(\n description=(\n \"Absolute path to a JSON file containing the MongoDB collecton of links \"\n \"entries (documents) to serve under the /links endpoint of the index \"\n \"meta-database. NB! As suggested in the previous sentence, these will \"\n \"only be served when using a MongoDB-based backend.\"\n ),\n ),\n ] = Path(__file__).parent.joinpath(\"index_links.json\")\n\n is_index: Annotated[\n Optional[bool],\n Field(\n description=(\n \"A runtime setting to dynamically switch between index meta-database and \"\n \"normal OPTIMADE servers. Used for switching behaviour of e.g., \"\n \"`meta->optimade_schema` values in the response. Any provided value may \"\n \"be overridden when used with the reference server implementation.\"\n ),\n ),\n ] = False\n\n schema_url: Annotated[\n Optional[Union[str, AnyHttpUrl]],\n Field(\n description=(\n \"A URL that will be provided in the `meta->schema` field for every response\"\n ),\n ),\n ] = f\"https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json\"\n\n custom_landing_page: Annotated[\n Optional[Union[str, Path]],\n Field(\n description=(\n \"The location of a custom landing page (Jinja template) to use for the API.\"\n ),\n ),\n ] = None\n\n index_schema_url: Annotated[\n Optional[Union[str, AnyHttpUrl]],\n Field(\n description=(\n \"A URL that will be provided in the `meta->schema` field for every \"\n \"response from the index meta-database.\"\n ),\n ),\n ] = f\"https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json\"\n\n log_level: Annotated[\n LogLevel, Field(description=\"Logging level for the OPTIMADE server.\")\n ] = LogLevel.INFO\n log_dir: Annotated[\n Path,\n Field(\n description=\"Folder in which log files will be saved.\",\n ),\n ] = Path(\"/var/log/optimade/\")\n validate_query_parameters: Annotated[\n Optional[bool],\n Field(\n description=(\n \"If True, the server will check whether the query parameters given in the \"\n \"request are correct.\"\n ),\n ),\n ] = True\n\n validate_api_response: Annotated[\n Optional[bool],\n Field(\n description=(\n \"If False, data from the database will not undergo validation before being\"\n \" emitted by the API, and only the mapping of aliases will occur.\"\n ),\n ),\n ] = True\n\n @field_validator(\"insert_from_jsonl\", mode=\"before\")\n @classmethod\n def check_jsonl_path(cls, value: Any) -> Optional[Path]:\n \"\"\"Check that the path to the JSONL file is valid.\"\"\"\n if value in (\"null\", \"\"):\n return None\n\n return value\n\n @field_validator(\"implementation\", mode=\"before\")\n @classmethod\n def set_implementation_version(cls, value: Any) -> dict[str, Any]:\n \"\"\"Set defaults and modify bypassed value(s)\"\"\"\n if not isinstance(value, dict):\n if isinstance(value, Implementation):\n value = value.model_dump()\n else:\n raise TypeError(\n f\"Expected a dict or Implementation instance, got {type(value)}\"\n )\n\n res = {\"version\": __version__}\n res.update(value)\n return res\n\n @model_validator(mode=\"after\")\n def use_real_mongo_override(self) -> \"ServerConfig\":\n \"\"\"Overrides the `database_backend` setting with MongoDB and\n raises a deprecation warning.\n \"\"\"\n use_real_mongo = self.use_real_mongo\n\n # Remove from model\n del self.use_real_mongo\n\n # Remove from set of user-defined fields\n if \"use_real_mongo\" in self.model_fields_set:\n self.model_fields_set.remove(\"use_real_mongo\")\n\n if use_real_mongo is not None:\n warnings.warn(\n \"'use_real_mongo' is deprecated, please set the appropriate 'database_backend' \"\n \"instead.\",\n DeprecationWarning,\n )\n\n if use_real_mongo:\n self.database_backend = SupportedBackend.MONGODB\n\n return self\n\n @classmethod\n def settings_customise_sources(\n cls,\n settings_cls: type[BaseSettings],\n init_settings: PydanticBaseSettingsSource,\n env_settings: PydanticBaseSettingsSource,\n dotenv_settings: PydanticBaseSettingsSource,\n file_secret_settings: PydanticBaseSettingsSource,\n ) -> tuple[PydanticBaseSettingsSource, ...]:\n \"\"\"\n **Priority of config settings sources**:\n\n 1. Passed arguments upon initialization of\n [`ServerConfig`][optimade.server.config.ServerConfig].\n 2. Environment variables, matching the syntax: `\"OPTIMADE_\"` or `\"optimade_\"` +\n `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or\n `optimade_log_dir=~/logs_dir/optimade/`.\n 3. Configuration file (JSON/YAML) taken from:\n 1. Environment variable `OPTIMADE_CONFIG_FILE`.\n 2. Default location (see\n [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).\n 4. Settings from secret file (see\n [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)\n for more information).\n\n \"\"\"\n return (\n init_settings,\n env_settings,\n ConfigFileSettingsSource(settings_cls),\n file_secret_settings,\n )\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.aliases","title":"aliases: Annotated[dict[Literal['links', 'references', 'structures'], dict[str, str]], Field(description='A mapping between field names in the database with their corresponding OPTIMADE field names, broken down by endpoint.')] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.base_url","title":"base_url: Annotated[Optional[str], Field(description='Base URL for this implementation')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.custom_landing_page","title":"custom_landing_page: Annotated[Optional[Union[str, Path]], Field(description='The location of a custom landing page (Jinja template) to use for the API.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.database_backend","title":"database_backend: Annotated[SupportedBackend, Field(description='Which database backend to use out of the supported backends.')] = SupportedBackend.MONGOMOCK
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.debug","title":"debug: Annotated[bool, Field(description='Turns on Debug Mode for the OPTIMADE Server implementation')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.default_db","title":"default_db: Annotated[str, Field(description='ID of /links endpoint resource for the chosen default OPTIMADE implementation (only relevant for the index meta-database)')] = 'test_server'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.elastic_hosts","title":"elastic_hosts: Annotated[Optional[Union[str, list[str], dict[str, Any], list[dict[str, Any]]]], Field(description='Host settings to pass through to the `Elasticsearch` class.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.implementation","title":"implementation: Annotated[Implementation, Field(description='Introspective information about this OPTIMADE implementation')] = Implementation(name='OPTIMADE Python Tools', version=__version__, source_url='https://github.com/Materials-Consortia/optimade-python-tools', maintainer={'email': 'dev@optimade.org'}, issue_tracker='https://github.com/Materials-Consortia/optimade-python-tools/issues', homepage='https://optimade.org/optimade-python-tools')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_base_url","title":"index_base_url: Annotated[Optional[AnyHttpUrl], Field(description='An optional link to the base URL for the index meta-database of the provider.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_links_path","title":"index_links_path: Annotated[Path, Field(description='Absolute path to a JSON file containing the MongoDB collecton of links entries (documents) to serve under the /links endpoint of the index meta-database. NB! As suggested in the previous sentence, these will only be served when using a MongoDB-based backend.')] = Path(__file__).parent.joinpath('index_links.json')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.index_schema_url","title":"index_schema_url: Annotated[Optional[Union[str, AnyHttpUrl]], Field(description='A URL that will be provided in the `meta->schema` field for every response from the index meta-database.')] = f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade_index.json'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.insert_from_jsonl","title":"insert_from_jsonl: Annotated[Optional[Path], Field(description='The absolute path to an OPTIMADE JSONL file to use to initialize the database. A unique index will be created over the ID to avoid duplication over multiple runs.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.insert_test_data","title":"insert_test_data: Annotated[bool, Field(description='Insert test data into each collection on server initialisation. If true, the configured backend will be populated with test data on server start. Should be disabled for production usage.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.is_index","title":"is_index: Annotated[Optional[bool], Field(description='A runtime setting to dynamically switch between index meta-database and normal OPTIMADE servers. Used for switching behaviour of e.g., `meta->optimade_schema` values in the response. Any provided value may be overridden when used with the reference server implementation.')] = False
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.length_aliases","title":"length_aliases: Annotated[dict[Literal['links', 'references', 'structures'], dict[str, str]], Field(description='A mapping between a list property (or otherwise) and an integer property that defines the length of that list, for example elements -> nelements. The standard aliases are applied first, so this dictionary must refer to the API fields, not the database fields.')] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.links_collection","title":"links_collection: Annotated[str, Field(description='Mongo collection name for /links endpoint resources')] = 'links'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.log_dir","title":"log_dir: Annotated[Path, Field(description='Folder in which log files will be saved.')] = Path('/var/log/optimade/')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.log_level","title":"log_level: Annotated[LogLevel, Field(description='Logging level for the OPTIMADE server.')] = LogLevel.INFO
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.model_config","title":"model_config = SettingsConfigDict(env_prefix='optimade_', extra='allow', env_file_encoding='utf-8', case_sensitive=False)
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_count_timeout","title":"mongo_count_timeout: Annotated[int, Field(description='Number of seconds to allow MongoDB to perform a full database count before falling back to `null`. This operation can require a full COLLSCAN for empty queries which can be prohibitively slow if the database does not fit into the active set, hence a timeout can drastically speed-up response times.')] = 5
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_database","title":"mongo_database: Annotated[str, Field(description='Mongo database for collection data')] = 'optimade'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.mongo_uri","title":"mongo_uri: Annotated[str, Field(description='URI for the Mongo server')] = 'localhost:27017'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.page_limit","title":"page_limit: Annotated[int, Field(description='Default number of resources per page')] = 20
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.page_limit_max","title":"page_limit_max: Annotated[int, Field(description='Max allowed number of resources per page')] = 500
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.provider","title":"provider: Annotated[Provider, Field(description='General information about the provider of this OPTIMADE implementation')] = Provider(prefix='exmpl', name='Example provider', description='Provider used for examples, not to be assigned to a real database', homepage='https://example.com')
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.provider_fields","title":"provider_fields: Annotated[dict[Literal['links', 'references', 'structures'], list[Union[str, dict[Literal['name', 'type', 'unit', 'description'], str]]]], Field(description=\"A list of additional fields to be served with the provider's prefix attached, broken down by endpoint.\")] = {}
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.references_collection","title":"references_collection: Annotated[str, Field(description='Mongo collection name for /references endpoint resources')] = 'references'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.root_path","title":"root_path: Annotated[Optional[str], Field(description='Sets the FastAPI app `root_path` parameter. This can be used to serve the API under a path prefix behind a proxy or as a sub-application of another FastAPI app. See https://fastapi.tiangolo.com/advanced/sub-applications/#technical-details-root_path for details.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.schema_url","title":"schema_url: Annotated[Optional[Union[str, AnyHttpUrl]], Field(description='A URL that will be provided in the `meta->schema` field for every response')] = f'https://schemas.optimade.org/openapi/v{__api_version__}/optimade.json'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.structures_collection","title":"structures_collection: Annotated[str, Field(description='Mongo collection name for /structures endpoint resources')] = 'structures'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.use_real_mongo","title":"use_real_mongo: Annotated[Optional[bool], Field(description='DEPRECATED: force usage of MongoDB over any other backend.')] = None
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.validate_api_response","title":"validate_api_response: Annotated[Optional[bool], Field(description='If False, data from the database will not undergo validation before being emitted by the API, and only the mapping of aliases will occur.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.validate_query_parameters","title":"validate_query_parameters: Annotated[Optional[bool], Field(description='If True, the server will check whether the query parameters given in the request are correct.')] = True
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.check_jsonl_path","title":"check_jsonl_path(value)
classmethod
","text":"Check that the path to the JSONL file is valid.
Source code inoptimade/server/config.py
@field_validator(\"insert_from_jsonl\", mode=\"before\")\n@classmethod\ndef check_jsonl_path(cls, value: Any) -> Optional[Path]:\n \"\"\"Check that the path to the JSONL file is valid.\"\"\"\n if value in (\"null\", \"\"):\n return None\n\n return value\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.set_implementation_version","title":"set_implementation_version(value)
classmethod
","text":"Set defaults and modify bypassed value(s)
Source code inoptimade/server/config.py
@field_validator(\"implementation\", mode=\"before\")\n@classmethod\ndef set_implementation_version(cls, value: Any) -> dict[str, Any]:\n \"\"\"Set defaults and modify bypassed value(s)\"\"\"\n if not isinstance(value, dict):\n if isinstance(value, Implementation):\n value = value.model_dump()\n else:\n raise TypeError(\n f\"Expected a dict or Implementation instance, got {type(value)}\"\n )\n\n res = {\"version\": __version__}\n res.update(value)\n return res\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.settings_customise_sources","title":"settings_customise_sources(settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings)
classmethod
","text":"Priority of config settings sources:
ServerConfig
.\"OPTIMADE_\"
or \"optimade_\"
+ <config_name>
, e.g., OPTIMADE_LOG_LEVEL=debug
or optimade_log_dir=~/logs_dir/optimade/
.OPTIMADE_CONFIG_FILE
.optimade/server/config.py
@classmethod\ndef settings_customise_sources(\n cls,\n settings_cls: type[BaseSettings],\n init_settings: PydanticBaseSettingsSource,\n env_settings: PydanticBaseSettingsSource,\n dotenv_settings: PydanticBaseSettingsSource,\n file_secret_settings: PydanticBaseSettingsSource,\n) -> tuple[PydanticBaseSettingsSource, ...]:\n \"\"\"\n **Priority of config settings sources**:\n\n 1. Passed arguments upon initialization of\n [`ServerConfig`][optimade.server.config.ServerConfig].\n 2. Environment variables, matching the syntax: `\"OPTIMADE_\"` or `\"optimade_\"` +\n `<config_name>`, e.g., `OPTIMADE_LOG_LEVEL=debug` or\n `optimade_log_dir=~/logs_dir/optimade/`.\n 3. Configuration file (JSON/YAML) taken from:\n 1. Environment variable `OPTIMADE_CONFIG_FILE`.\n 2. Default location (see\n [DEFAULT_CONFIG_FILE_PATH][optimade.server.config.DEFAULT_CONFIG_FILE_PATH]).\n 4. Settings from secret file (see\n [pydantic documentation](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support)\n for more information).\n\n \"\"\"\n return (\n init_settings,\n env_settings,\n ConfigFileSettingsSource(settings_cls),\n file_secret_settings,\n )\n
"},{"location":"api_reference/server/config/#optimade.server.config.ServerConfig.use_real_mongo_override","title":"use_real_mongo_override()
","text":"Overrides the database_backend
setting with MongoDB and raises a deprecation warning.
optimade/server/config.py
@model_validator(mode=\"after\")\ndef use_real_mongo_override(self) -> \"ServerConfig\":\n \"\"\"Overrides the `database_backend` setting with MongoDB and\n raises a deprecation warning.\n \"\"\"\n use_real_mongo = self.use_real_mongo\n\n # Remove from model\n del self.use_real_mongo\n\n # Remove from set of user-defined fields\n if \"use_real_mongo\" in self.model_fields_set:\n self.model_fields_set.remove(\"use_real_mongo\")\n\n if use_real_mongo is not None:\n warnings.warn(\n \"'use_real_mongo' is deprecated, please set the appropriate 'database_backend' \"\n \"instead.\",\n DeprecationWarning,\n )\n\n if use_real_mongo:\n self.database_backend = SupportedBackend.MONGODB\n\n return self\n
"},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend","title":"SupportedBackend
","text":" Bases: Enum
Enumeration of supported database backends
elastic
: Elasticsearch.mongodb
: MongoDB.mongomock
: Also MongoDB, but instead of using the pymongo
driver to connect to a live Mongo database instance, this will use the mongomock
driver, creating an in-memory database, which is mainly used for testing.optimade/server/config.py
class SupportedBackend(Enum):\n \"\"\"Enumeration of supported database backends\n\n - `elastic`: [Elasticsearch](https://www.elastic.co/).\n - `mongodb`: [MongoDB](https://www.mongodb.com/).\n - `mongomock`: Also MongoDB, but instead of using the\n [`pymongo`](https://pymongo.readthedocs.io/) driver to connect to a live Mongo\n database instance, this will use the\n [`mongomock`](https://github.com/mongomock/mongomock) driver, creating an\n in-memory database, which is mainly used for testing.\n\n \"\"\"\n\n ELASTIC = \"elastic\"\n MONGODB = \"mongodb\"\n MONGOMOCK = \"mongomock\"\n
"},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.ELASTIC","title":"ELASTIC = 'elastic'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.MONGODB","title":"MONGODB = 'mongodb'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/config/#optimade.server.config.SupportedBackend.MONGOMOCK","title":"MONGOMOCK = 'mongomock'
class-attribute
instance-attribute
","text":""},{"location":"api_reference/server/exception_handlers/","title":"exception_handlers","text":""},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.OPTIMADE_EXCEPTIONS","title":"OPTIMADE_EXCEPTIONS: Iterable[tuple[type[Exception], Callable[[Request, Exception], JSONAPIResponse]]] = [(StarletteHTTPException, http_exception_handler), (OptimadeHTTPException, http_exception_handler), (RequestValidationError, request_validation_exception_handler), (ValidationError, validation_exception_handler), (VisitError, grammar_not_implemented_handler), (NotImplementedError, not_implemented_handler), (Exception, general_exception_handler)]
module-attribute
","text":"A tuple of all pairs of exceptions and handler functions that allow for appropriate responses to be returned in certain scenarios according to the OPTIMADE specification.
To use these in FastAPI app code:
from fastapi import FastAPI\napp = FastAPI()\nfor exception, handler in OPTIMADE_EXCEPTIONS:\n app.add_exception_handler(exception, handler)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.general_exception","title":"general_exception(request, exc, status_code=500, errors=None)
","text":"Handle an exception
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Exception
The exception being raised.
requiredstatus_code
int
The returned HTTP status code for the error response.
500
errors
Optional[list[OptimadeError]]
List of error resources as defined in the OPTIMADE specification.
None
Returns:
Type DescriptionJSONAPIResponse
A JSON HTTP response based on ErrorResponse
.
optimade/server/exception_handlers.py
def general_exception(\n request: Request,\n exc: Exception,\n status_code: int = 500, # A status_code in `exc` will take precedence\n errors: Optional[list[OptimadeError]] = None,\n) -> JSONAPIResponse:\n \"\"\"Handle an exception\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n status_code: The returned HTTP status code for the error response.\n errors: List of error resources as defined in\n [the OPTIMADE specification](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#json-response-schema-common-fields).\n\n Returns:\n A JSON HTTP response based on [`ErrorResponse`][optimade.models.responses.ErrorResponse].\n\n \"\"\"\n debug_info = {}\n if CONFIG.debug:\n tb = \"\".join(\n traceback.format_exception(type(exc), value=exc, tb=exc.__traceback__)\n )\n LOGGER.error(\"Traceback:\\n%s\", tb)\n debug_info[f\"_{CONFIG.provider.prefix}_traceback\"] = tb\n\n try:\n http_response_code = int(exc.status_code) # type: ignore[attr-defined]\n except AttributeError:\n http_response_code = int(status_code)\n\n try:\n title = str(exc.title) # type: ignore[attr-defined]\n except AttributeError:\n title = str(exc.__class__.__name__)\n\n try:\n detail = str(exc.detail) # type: ignore[attr-defined]\n except AttributeError:\n detail = str(exc)\n\n if errors is None:\n errors = [OptimadeError(detail=detail, status=http_response_code, title=title)]\n\n response = ErrorResponse(\n meta=meta_values(\n url=request.url,\n data_returned=0,\n data_available=0,\n more_data_available=False,\n schema=CONFIG.schema_url,\n **debug_info,\n ),\n errors=errors,\n )\n\n return JSONAPIResponse(\n status_code=http_response_code,\n content=jsonable_encoder(response, exclude_unset=True),\n )\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.general_exception_handler","title":"general_exception_handler(request, exc)
","text":"Catch all Python Exceptions not handled by other exception handlers
Pass-through directly to general_exception()
.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Exception
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def general_exception_handler(request: Request, exc: Exception) -> JSONAPIResponse:\n \"\"\"Catch all Python Exceptions not handled by other exception handlers\n\n Pass-through directly to [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.grammar_not_implemented_handler","title":"grammar_not_implemented_handler(request, exc)
","text":"Handle an error raised by Lark during filter transformation
All errors raised during filter transformation are wrapped in the Lark VisitError
. According to the OPTIMADE specification, these errors are repurposed to be 501 NotImplementedErrors.
For special exceptions, like BadRequest
, we pass-through to general_exception()
, since they should not return a 501 NotImplementedError.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
VisitError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def grammar_not_implemented_handler(\n request: Request, exc: VisitError\n) -> JSONAPIResponse:\n \"\"\"Handle an error raised by Lark during filter transformation\n\n All errors raised during filter transformation are wrapped in the Lark `VisitError`.\n According to the OPTIMADE specification, these errors are repurposed to be 501 NotImplementedErrors.\n\n For special exceptions, like [`BadRequest`][optimade.exceptions.BadRequest], we pass-through to\n [`general_exception()`][optimade.server.exception_handlers.general_exception], since they should not\n return a 501 NotImplementedError.\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n pass_through_exceptions = (BadRequest,)\n\n orig_exc = getattr(exc, \"orig_exc\", None)\n if isinstance(orig_exc, pass_through_exceptions):\n return general_exception(request, orig_exc)\n\n rule = getattr(exc.obj, \"data\", getattr(exc.obj, \"type\", str(exc)))\n\n status = 501\n title = \"NotImplementedError\"\n detail = (\n f\"Error trying to process rule '{rule}'\"\n if not str(exc.orig_exc)\n else str(exc.orig_exc)\n )\n error = OptimadeError(detail=detail, status=status, title=title)\n return general_exception(request, exc, status_code=status, errors=[error])\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.http_exception_handler","title":"http_exception_handler(request, exc)
","text":"Handle a general HTTP Exception from Starlette
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
Union[StarletteHTTPException, OptimadeHTTPException]
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def http_exception_handler(\n request: Request,\n exc: Union[StarletteHTTPException, OptimadeHTTPException],\n) -> JSONAPIResponse:\n \"\"\"Handle a general HTTP Exception from Starlette\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.not_implemented_handler","title":"not_implemented_handler(request, exc)
","text":"Handle a standard NotImplementedError Python exception
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
NotImplementedError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def not_implemented_handler(\n request: Request, exc: NotImplementedError\n) -> JSONAPIResponse:\n \"\"\"Handle a standard NotImplementedError Python exception\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n status = 501\n title = \"NotImplementedError\"\n detail = str(exc)\n error = OptimadeError(detail=detail, status=status, title=title)\n return general_exception(request, exc, status_code=status, errors=[error])\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.request_validation_exception_handler","title":"request_validation_exception_handler(request, exc)
","text":"Handle a request validation error from FastAPI
RequestValidationError
is a specialization of a general pydantic ValidationError
. Pass-through directly to general_exception()
.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
RequestValidationError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def request_validation_exception_handler(\n request: Request, exc: RequestValidationError\n) -> JSONAPIResponse:\n \"\"\"Handle a request validation error from FastAPI\n\n `RequestValidationError` is a specialization of a general pydantic `ValidationError`.\n Pass-through directly to [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n return general_exception(request, exc)\n
"},{"location":"api_reference/server/exception_handlers/#optimade.server.exception_handlers.validation_exception_handler","title":"validation_exception_handler(request, exc)
","text":"Handle a general pydantic validation error
The pydantic ValidationError
usually contains a list of errors, this function extracts them and wraps them in the OPTIMADE specific error resource.
Parameters:
Name Type Description Defaultrequest
Request
The HTTP request resulting in the exception being raised.
requiredexc
ValidationError
The exception being raised.
requiredReturns:
Type DescriptionJSONAPIResponse
A JSON HTTP response through general_exception()
.
optimade/server/exception_handlers.py
def validation_exception_handler(\n request: Request, exc: ValidationError\n) -> JSONAPIResponse:\n \"\"\"Handle a general pydantic validation error\n\n The pydantic `ValidationError` usually contains a list of errors,\n this function extracts them and wraps them in the OPTIMADE specific error resource.\n\n Parameters:\n request: The HTTP request resulting in the exception being raised.\n exc: The exception being raised.\n\n Returns:\n A JSON HTTP response through [`general_exception()`][optimade.server.exception_handlers.general_exception].\n\n \"\"\"\n status = 500\n title = \"ValidationError\"\n errors = set()\n for error in exc.errors():\n pointer = \"/\" + \"/\".join([str(_) for _ in error[\"loc\"]])\n source = ErrorSource(pointer=pointer)\n code = error[\"type\"]\n detail = error[\"msg\"]\n errors.add(\n OptimadeError(\n detail=detail, status=status, title=title, source=source, code=code\n )\n )\n return general_exception(request, exc, status_code=status, errors=list(errors))\n
"},{"location":"api_reference/server/exceptions/","title":"exceptions","text":"Reproduced imports from optimade.exceptions
for backwards-compatibility.
BadRequest
","text":" Bases: OptimadeHTTPException
400 Bad Request
Source code inoptimade/exceptions.py
class BadRequest(OptimadeHTTPException):\n \"\"\"400 Bad Request\"\"\"\n\n status_code: int = 400\n title: str = \"Bad Request\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.Forbidden","title":"Forbidden
","text":" Bases: OptimadeHTTPException
403 Forbidden
Source code inoptimade/exceptions.py
class Forbidden(OptimadeHTTPException):\n \"\"\"403 Forbidden\"\"\"\n\n status_code: int = 403\n title: str = \"Forbidden\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.InternalServerError","title":"InternalServerError
","text":" Bases: OptimadeHTTPException
500 Internal Server Error
Source code inoptimade/exceptions.py
class InternalServerError(OptimadeHTTPException):\n \"\"\"500 Internal Server Error\"\"\"\n\n status_code: int = 500\n title: str = \"Internal Server Error\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.NotFound","title":"NotFound
","text":" Bases: OptimadeHTTPException
404 Not Found
Source code inoptimade/exceptions.py
class NotFound(OptimadeHTTPException):\n \"\"\"404 Not Found\"\"\"\n\n status_code: int = 404\n title: str = \"Not Found\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.NotImplementedResponse","title":"NotImplementedResponse
","text":" Bases: OptimadeHTTPException
501 Not Implemented
Source code inoptimade/exceptions.py
class NotImplementedResponse(OptimadeHTTPException):\n \"\"\"501 Not Implemented\"\"\"\n\n status_code: int = 501\n title: str = \"Not Implemented\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.OptimadeHTTPException","title":"OptimadeHTTPException
","text":" Bases: Exception
, ABC
This abstract class can be subclassed to define HTTP responses with the desired status codes, and detailed error strings to represent in the JSON:API error response.
This class closely follows the starlette.HTTPException
without requiring it as a dependency, so that such errors can also be raised from within client code.
Attributes:
Name Type Descriptionstatus_code
int
The HTTP status code accompanying this exception.
title
str
A descriptive title for this exception.
detail
Optional[str]
An optional string containing the details of the error.
Source code inoptimade/exceptions.py
class OptimadeHTTPException(Exception, ABC):\n \"\"\"This abstract class can be subclassed to define\n HTTP responses with the desired status codes, and\n detailed error strings to represent in the JSON:API\n error response.\n\n This class closely follows the `starlette.HTTPException` without\n requiring it as a dependency, so that such errors can also be\n raised from within client code.\n\n Attributes:\n status_code: The HTTP status code accompanying this exception.\n title: A descriptive title for this exception.\n detail: An optional string containing the details of the error.\n\n \"\"\"\n\n status_code: int\n title: str\n detail: Optional[str] = None\n headers: Optional[dict[str, Any]] = None\n\n def __init__(\n self, detail: Optional[str] = None, headers: Optional[dict] = None\n ) -> None:\n if self.status_code is None:\n raise AttributeError(\n \"HTTPException class {self.__class__.__name__} is missing required `status_code` attribute.\"\n )\n self.detail = detail\n self.headers = headers\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else self.__repr__()\n\n def __repr__(self) -> str:\n class_name = self.__class__.__name__\n return f\"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.UnprocessableEntity","title":"UnprocessableEntity
","text":" Bases: OptimadeHTTPException
422 Unprocessable Entity
Source code inoptimade/exceptions.py
class UnprocessableEntity(OptimadeHTTPException):\n \"\"\"422 Unprocessable Entity\"\"\"\n\n status_code: int = 422\n title: str = \"Unprocessable Entity\"\n
"},{"location":"api_reference/server/exceptions/#optimade.server.exceptions.VersionNotSupported","title":"VersionNotSupported
","text":" Bases: OptimadeHTTPException
553 Version Not Supported
Source code inoptimade/exceptions.py
class VersionNotSupported(OptimadeHTTPException):\n \"\"\"553 Version Not Supported\"\"\"\n\n status_code: int = 553\n title: str = \"Version Not Supported\"\n
"},{"location":"api_reference/server/logger/","title":"logger","text":"Logging to both file and terminal
"},{"location":"api_reference/server/main/","title":"main","text":"The OPTIMADE server
The server is based on MongoDB, using either pymongo
or mongomock
.
This is an example implementation with example data. To implement your own server see the documentation at https://optimade.org/optimade-python-tools.
"},{"location":"api_reference/server/main/#optimade.server.main.add_major_version_base_url","title":"add_major_version_base_url(app)
","text":"Add mandatory vMajor endpoints, i.e. all except versions.
Source code inoptimade/server/main.py
def add_major_version_base_url(app: FastAPI):\n \"\"\"Add mandatory vMajor endpoints, i.e. all except versions.\"\"\"\n for endpoint in (info, links, references, structures, landing):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[\"major\"])\n
"},{"location":"api_reference/server/main/#optimade.server.main.add_optional_versioned_base_urls","title":"add_optional_versioned_base_urls(app)
","text":"Add the following OPTIONAL prefixes/base URLs to server:
/vMajor.Minor\n /vMajor.Minor.Patch\n
Source code in optimade/server/main.py
def add_optional_versioned_base_urls(app: FastAPI):\n \"\"\"Add the following OPTIONAL prefixes/base URLs to server:\n ```\n /vMajor.Minor\n /vMajor.Minor.Patch\n ```\n \"\"\"\n for version in (\"minor\", \"patch\"):\n for endpoint in (info, links, references, structures, landing):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[version])\n
"},{"location":"api_reference/server/main/#optimade.server.main.lifespan","title":"lifespan(app)
async
","text":"Add dynamic endpoints on startup.
Source code inoptimade/server/main.py
@asynccontextmanager # type: ignore[arg-type]\nasync def lifespan(app: FastAPI):\n \"\"\"Add dynamic endpoints on startup.\"\"\"\n # Add API endpoints for MANDATORY base URL `/vMAJOR`\n add_major_version_base_url(app)\n # Add API endpoints for OPTIONAL base URLs `/vMAJOR.MINOR` and `/vMAJOR.MINOR.PATCH`\n add_optional_versioned_base_urls(app)\n\n # Yield so that the app can start\n yield\n
"},{"location":"api_reference/server/main_index/","title":"main_index","text":"The OPTIMADE Index Meta-Database server
The server is based on MongoDB, using either pymongo
or mongomock
.
This is an example implementation with example data. To implement your own index meta-database server see the documentation at https://optimade.org/optimade-python-tools.
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.add_major_version_base_url","title":"add_major_version_base_url(app)
","text":"Add mandatory endpoints to /vMAJOR
base URL.
optimade/server/main_index.py
def add_major_version_base_url(app: FastAPI):\n \"\"\"Add mandatory endpoints to `/vMAJOR` base URL.\"\"\"\n for endpoint in (index_info, links):\n app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[\"major\"])\n
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.add_optional_versioned_base_urls","title":"add_optional_versioned_base_urls(app)
","text":"Add the following OPTIONAL prefixes/base URLs to server:
/vMajor.Minor\n /vMajor.Minor.Patch\n
Source code in optimade/server/main_index.py
def add_optional_versioned_base_urls(app: FastAPI):\n \"\"\"Add the following OPTIONAL prefixes/base URLs to server:\n ```\n /vMajor.Minor\n /vMajor.Minor.Patch\n ```\n \"\"\"\n for version in (\"minor\", \"patch\"):\n app.include_router(index_info.router, prefix=BASE_URL_PREFIXES[version])\n app.include_router(links.router, prefix=BASE_URL_PREFIXES[version])\n
"},{"location":"api_reference/server/main_index/#optimade.server.main_index.lifespan","title":"lifespan(app)
async
","text":"Add dynamic endpoints and adjust config on startup.
Source code inoptimade/server/main_index.py
@asynccontextmanager # type: ignore[arg-type]\nasync def lifespan(app: FastAPI):\n \"\"\"Add dynamic endpoints and adjust config on startup.\"\"\"\n CONFIG.is_index = True\n # Add API endpoints for MANDATORY base URL `/vMAJOR`\n add_major_version_base_url(app)\n # Add API endpoints for OPTIONAL base URLs `/vMAJOR.MINOR` and `/vMAJOR.MINOR.PATCH`\n add_optional_versioned_base_urls(app)\n\n # Yield so that the app can start\n yield\n
"},{"location":"api_reference/server/middleware/","title":"middleware","text":"Custom ASGI app middleware.
These middleware are based on Starlette's BaseHTTPMiddleware
. See the specific Starlette documentation page for more information on it's middleware implementation.
OPTIMADE_MIDDLEWARE: Iterable[BaseHTTPMiddleware] = (EnsureQueryParamIntegrity, CheckWronglyVersionedBaseUrls, HandleApiHint, AddWarnings)
module-attribute
","text":"A tuple of all the middleware classes that implement certain required features of the OPTIMADE specification, e.g. warnings and URL versioning.
Note
The order in which middleware is added to an application matters.
As discussed in the docstring of AddWarnings
, this middleware is the final entry to this list so that it is the first to be applied by the server. Any other middleware should therefore be added before iterating through this variable. This is the opposite way around to the example in the Starlette documentation which initialises the application with a pre-built middleware list in the reverse order to OPTIMADE_MIDDLEWARE
.
To use this variable in FastAPI app code after initialisation:
from fastapi import FastAPI\napp = FastAPI()\nfor middleware in OPTIMADE_MIDDLEWARE:\n app.add_middleware(middleware)\n
Alternatively, to use this variable on initialisation:
from fastapi import FastAPI\nfrom starlette.middleware import Middleware\napp = FastAPI(\n ...,\n middleware=[Middleware(m) for m in reversed(OPTIMADE_MIDDLEWARE)]\n)\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings","title":"AddWarnings
","text":" Bases: BaseHTTPMiddleware
Add OptimadeWarning
s to the response.
All sub-classes of OptimadeWarning
will also be added to the response's meta.warnings
list.
By overriding the warnings.showwarning()
function with the showwarning
method, all usages of warnings.warn()
will result in the regular printing of the warning message to stderr
, but also its addition to an in-memory list of warnings. This middleware will, after the URL request has been handled, add the list of accumulated warnings to the JSON response under the meta.warnings
field.
To make sure the last part happens correctly and a Starlette StreamingResponse
is returned, as is expected from a BaseHTTPMiddleware
sub-class, one is instantiated with the updated Content-Length
header, as well as making sure the response's body content is actually streamable, by breaking it down into chunks of the original response's chunk size.
Important
It is recommended to add this middleware as the last one to your application.
This is to ensure it is invoked first, updating warnings.showwarning()
and catching all warnings that should be added to the response.
This can be achieved by applying AddWarnings
after all other middleware with the .add_middleware()
method, or by initialising the app with a middleware list in which AddWarnings
appears first. More information can be found in the docstring of OPTIMADE_MIDDLEWARE
.
Attributes:
Name Type Description_warnings
List[Warnings]
List of Warnings
added through usages of warnings.warn()
via showwarning
.
optimade/server/middleware.py
class AddWarnings(BaseHTTPMiddleware):\n \"\"\"\n Add [`OptimadeWarning`][optimade.warnings.OptimadeWarning]s to the response.\n\n All sub-classes of [`OptimadeWarning`][optimade.warnings.OptimadeWarning]\n will also be added to the response's\n [`meta.warnings`][optimade.models.optimade_json.ResponseMeta.warnings] list.\n\n By overriding the `warnings.showwarning()` function with the\n [`showwarning` method][optimade.server.middleware.AddWarnings.showwarning],\n all usages of `warnings.warn()` will result in the regular printing of the\n warning message to `stderr`, but also its addition to an in-memory list of\n warnings.\n This middleware will, after the URL request has been handled, add the list of\n accumulated warnings to the JSON response under the\n [`meta.warnings`][optimade.models.optimade_json.ResponseMeta.warnings] field.\n\n To make sure the last part happens correctly and a Starlette `StreamingResponse`\n is returned, as is expected from a `BaseHTTPMiddleware` sub-class, one is\n instantiated with the updated `Content-Length` header, as well as making sure\n the response's body content is actually streamable, by breaking it down into\n chunks of the original response's chunk size.\n\n !!! warning \"Important\"\n It is **recommended** to add this middleware as the _last one_ to your application.\n\n This is to ensure it is invoked _first_, updating `warnings.showwarning()` and\n catching all warnings that should be added to the response.\n\n This can be achieved by applying `AddWarnings` _after_ all\n other middleware with the `.add_middleware()` method, or by\n initialising the app with a middleware list in which `AddWarnings`\n appears _first_. More information can be found in the docstring of\n [`OPTIMADE_MIDDLEWARE`][optimade.server.middleware.OPTIMADE_MIDDLEWARE].\n\n Attributes:\n _warnings (List[Warnings]): List of [`Warnings`][optimade.models.optimade_json.Warnings]\n added through usages of `warnings.warn()` via [`showwarning`][optimade.server.middleware.AddWarnings.showwarning].\n\n \"\"\"\n\n _warnings: list[Warnings]\n\n def showwarning(\n self,\n message: Union[Warning, str],\n category: type[Warning],\n filename: str,\n lineno: int,\n file: Optional[TextIO] = None,\n line: Optional[str] = None,\n ) -> None:\n \"\"\"\n Hook to write a warning to a file using the built-in `warnings` lib.\n\n In [the documentation](https://docs.python.org/3/library/warnings.html)\n for the built-in `warnings` library, there are a few recommended ways of\n customizing the printing of warning messages.\n\n This method can override the `warnings.showwarning` function,\n which is called as part of the `warnings` library's workflow to print\n warning messages, e.g., when using `warnings.warn()`.\n Originally, it prints warning messages to `stderr`.\n This method will also print warning messages to `stderr` by calling\n `warnings._showwarning_orig()` or `warnings._showwarnmsg_impl()`.\n The first function will be called if the issued warning is not recognized\n as an [`OptimadeWarning`][optimade.warnings.OptimadeWarning].\n This is equivalent to \"standard behaviour\".\n The second function will be called _after_ an\n [`OptimadeWarning`][optimade.warnings.OptimadeWarning] has been handled.\n\n An [`OptimadeWarning`][optimade.warnings.OptimadeWarning] will be\n translated into an OPTIMADE Warnings JSON object in accordance with\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#json-response-schema-common-fields).\n This process is similar to the [Exception handlers][optimade.server.exception_handlers].\n\n Parameters:\n message: The `Warning` object to show and possibly handle.\n category: `Warning` type being warned about. This amounts to `type(message)`.\n filename: Name of the file, where the warning was issued.\n lineno: Line number in the file, where the warning was issued.\n file: A file-like object to which the warning should be written.\n line: Source content of the line that issued the warning.\n\n \"\"\"\n assert isinstance(\n message, Warning\n ), \"'message' is expected to be a Warning or subclass thereof.\"\n\n if not isinstance(message, OptimadeWarning):\n # If the Warning is not an OptimadeWarning or subclass thereof,\n # use the regular 'showwarning' function.\n warnings._showwarning_orig(message, category, filename, lineno, file, line) # type: ignore[attr-defined]\n return\n\n if isinstance(message, LocalOptimadeWarning):\n return\n\n # Format warning\n try:\n title = str(message.title)\n except AttributeError:\n title = str(message.__class__.__name__)\n\n try:\n detail = str(message.detail)\n except AttributeError:\n detail = str(message)\n\n if CONFIG.debug:\n if line is None:\n # All this is taken directly from the warnings library.\n # See 'warnings._formatwarnmsg_impl()' for the original code.\n try:\n import linecache\n\n line = linecache.getline(filename, lineno)\n except Exception:\n # When a warning is logged during Python shutdown, linecache\n # and the import machinery don't work anymore\n line = None\n meta = {\n \"filename\": filename,\n \"lineno\": lineno,\n }\n if line:\n meta[\"line\"] = line.strip()\n\n if CONFIG.debug:\n new_warning = Warnings(title=title, detail=detail, meta=meta)\n else:\n new_warning = Warnings(title=title, detail=detail)\n\n # Add new warning to self._warnings\n self._warnings.append(new_warning.model_dump(exclude_unset=True))\n\n # Show warning message as normal in sys.stderr\n warnings._showwarnmsg_impl( # type: ignore[attr-defined]\n warnings.WarningMessage(message, category, filename, lineno, file, line)\n )\n\n @staticmethod\n def chunk_it_up(content: Union[str, bytes], chunk_size: int) -> Generator:\n \"\"\"Return generator for string in chunks of size `chunk_size`.\n\n Parameters:\n content: String or bytes content to separate into chunks.\n chunk_size: The size of the chunks, i.e. the length of the string-chunks.\n\n Returns:\n A Python generator to be converted later to an `asyncio` generator.\n\n \"\"\"\n if chunk_size <= 0:\n chunk_size = 1\n return (content[i : chunk_size + i] for i in range(0, len(content), chunk_size))\n\n async def dispatch(self, request: Request, call_next):\n self._warnings = []\n\n warnings.simplefilter(action=\"default\", category=OptimadeWarning)\n warnings.showwarning = self.showwarning\n\n response = await call_next(request)\n\n status = response.status_code\n headers = response.headers\n media_type = response.media_type\n background = response.background\n charset = response.charset\n\n body = b\"\"\n chunk_size = 0\n async for chunk in response.body_iterator:\n chunk_size = chunk_size or len(chunk)\n if not isinstance(chunk, bytes):\n chunk = chunk.encode(charset)\n body += chunk\n body_str = body.decode(charset)\n\n if self._warnings:\n response = json.loads(body_str)\n response.get(\"meta\", {})[\"warnings\"] = self._warnings\n body_str = json.dumps(response)\n if \"content-length\" in headers:\n headers[\"content-length\"] = str(len(body_str))\n\n response = StreamingResponse(\n content=self.chunk_it_up(body_str, chunk_size),\n status_code=status,\n headers=headers,\n media_type=media_type,\n background=background,\n )\n\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings.chunk_it_up","title":"chunk_it_up(content, chunk_size)
staticmethod
","text":"Return generator for string in chunks of size chunk_size
.
Parameters:
Name Type Description Defaultcontent
Union[str, bytes]
String or bytes content to separate into chunks.
requiredchunk_size
int
The size of the chunks, i.e. the length of the string-chunks.
requiredReturns:
Type DescriptionGenerator
A Python generator to be converted later to an asyncio
generator.
optimade/server/middleware.py
@staticmethod\ndef chunk_it_up(content: Union[str, bytes], chunk_size: int) -> Generator:\n \"\"\"Return generator for string in chunks of size `chunk_size`.\n\n Parameters:\n content: String or bytes content to separate into chunks.\n chunk_size: The size of the chunks, i.e. the length of the string-chunks.\n\n Returns:\n A Python generator to be converted later to an `asyncio` generator.\n\n \"\"\"\n if chunk_size <= 0:\n chunk_size = 1\n return (content[i : chunk_size + i] for i in range(0, len(content), chunk_size))\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.AddWarnings.showwarning","title":"showwarning(message, category, filename, lineno, file=None, line=None)
","text":"Hook to write a warning to a file using the built-in warnings
lib.
In the documentation for the built-in warnings
library, there are a few recommended ways of customizing the printing of warning messages.
This method can override the warnings.showwarning
function, which is called as part of the warnings
library's workflow to print warning messages, e.g., when using warnings.warn()
. Originally, it prints warning messages to stderr
. This method will also print warning messages to stderr
by calling warnings._showwarning_orig()
or warnings._showwarnmsg_impl()
. The first function will be called if the issued warning is not recognized as an OptimadeWarning
. This is equivalent to \"standard behaviour\". The second function will be called after an OptimadeWarning
has been handled.
An OptimadeWarning
will be translated into an OPTIMADE Warnings JSON object in accordance with the specification. This process is similar to the Exception handlers.
Parameters:
Name Type Description Defaultmessage
Union[Warning, str]
The Warning
object to show and possibly handle.
category
type[Warning]
Warning
type being warned about. This amounts to type(message)
.
filename
str
Name of the file, where the warning was issued.
requiredlineno
int
Line number in the file, where the warning was issued.
requiredfile
Optional[TextIO]
A file-like object to which the warning should be written.
None
line
Optional[str]
Source content of the line that issued the warning.
None
Source code in optimade/server/middleware.py
def showwarning(\n self,\n message: Union[Warning, str],\n category: type[Warning],\n filename: str,\n lineno: int,\n file: Optional[TextIO] = None,\n line: Optional[str] = None,\n) -> None:\n \"\"\"\n Hook to write a warning to a file using the built-in `warnings` lib.\n\n In [the documentation](https://docs.python.org/3/library/warnings.html)\n for the built-in `warnings` library, there are a few recommended ways of\n customizing the printing of warning messages.\n\n This method can override the `warnings.showwarning` function,\n which is called as part of the `warnings` library's workflow to print\n warning messages, e.g., when using `warnings.warn()`.\n Originally, it prints warning messages to `stderr`.\n This method will also print warning messages to `stderr` by calling\n `warnings._showwarning_orig()` or `warnings._showwarnmsg_impl()`.\n The first function will be called if the issued warning is not recognized\n as an [`OptimadeWarning`][optimade.warnings.OptimadeWarning].\n This is equivalent to \"standard behaviour\".\n The second function will be called _after_ an\n [`OptimadeWarning`][optimade.warnings.OptimadeWarning] has been handled.\n\n An [`OptimadeWarning`][optimade.warnings.OptimadeWarning] will be\n translated into an OPTIMADE Warnings JSON object in accordance with\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#json-response-schema-common-fields).\n This process is similar to the [Exception handlers][optimade.server.exception_handlers].\n\n Parameters:\n message: The `Warning` object to show and possibly handle.\n category: `Warning` type being warned about. This amounts to `type(message)`.\n filename: Name of the file, where the warning was issued.\n lineno: Line number in the file, where the warning was issued.\n file: A file-like object to which the warning should be written.\n line: Source content of the line that issued the warning.\n\n \"\"\"\n assert isinstance(\n message, Warning\n ), \"'message' is expected to be a Warning or subclass thereof.\"\n\n if not isinstance(message, OptimadeWarning):\n # If the Warning is not an OptimadeWarning or subclass thereof,\n # use the regular 'showwarning' function.\n warnings._showwarning_orig(message, category, filename, lineno, file, line) # type: ignore[attr-defined]\n return\n\n if isinstance(message, LocalOptimadeWarning):\n return\n\n # Format warning\n try:\n title = str(message.title)\n except AttributeError:\n title = str(message.__class__.__name__)\n\n try:\n detail = str(message.detail)\n except AttributeError:\n detail = str(message)\n\n if CONFIG.debug:\n if line is None:\n # All this is taken directly from the warnings library.\n # See 'warnings._formatwarnmsg_impl()' for the original code.\n try:\n import linecache\n\n line = linecache.getline(filename, lineno)\n except Exception:\n # When a warning is logged during Python shutdown, linecache\n # and the import machinery don't work anymore\n line = None\n meta = {\n \"filename\": filename,\n \"lineno\": lineno,\n }\n if line:\n meta[\"line\"] = line.strip()\n\n if CONFIG.debug:\n new_warning = Warnings(title=title, detail=detail, meta=meta)\n else:\n new_warning = Warnings(title=title, detail=detail)\n\n # Add new warning to self._warnings\n self._warnings.append(new_warning.model_dump(exclude_unset=True))\n\n # Show warning message as normal in sys.stderr\n warnings._showwarnmsg_impl( # type: ignore[attr-defined]\n warnings.WarningMessage(message, category, filename, lineno, file, line)\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.CheckWronglyVersionedBaseUrls","title":"CheckWronglyVersionedBaseUrls
","text":" Bases: BaseHTTPMiddleware
If a non-supported versioned base URL is supplied return 553 Version Not Supported
.
optimade/server/middleware.py
class CheckWronglyVersionedBaseUrls(BaseHTTPMiddleware):\n \"\"\"If a non-supported versioned base URL is supplied return `553 Version Not Supported`.\"\"\"\n\n @staticmethod\n def check_url(url: StarletteURL):\n \"\"\"Check URL path for versioned part.\n\n Parameters:\n url: A complete urllib-parsed raw URL.\n\n Raises:\n VersionNotSupported: If the URL represents an OPTIMADE versioned base URL\n and the version part is not supported by the implementation.\n\n \"\"\"\n base_url = get_base_url(url)\n optimade_path = f\"{url.scheme}://{url.netloc}{url.path}\"[len(base_url) :]\n match = re.match(r\"^(?P<version>/v[0-9]+(\\.[0-9]+){0,2}).*\", optimade_path)\n if match is not None:\n if match.group(\"version\") not in BASE_URL_PREFIXES.values():\n raise VersionNotSupported(\n detail=(\n f\"The parsed versioned base URL {match.group('version')!r} from \"\n f\"{url} is not supported by this implementation. \"\n f\"Supported versioned base URLs are: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n\n async def dispatch(self, request: Request, call_next):\n if request.url.path:\n self.check_url(request.url)\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.CheckWronglyVersionedBaseUrls.check_url","title":"check_url(url)
staticmethod
","text":"Check URL path for versioned part.
Parameters:
Name Type Description Defaulturl
URL
A complete urllib-parsed raw URL.
requiredRaises:
Type DescriptionVersionNotSupported
If the URL represents an OPTIMADE versioned base URL and the version part is not supported by the implementation.
Source code inoptimade/server/middleware.py
@staticmethod\ndef check_url(url: StarletteURL):\n \"\"\"Check URL path for versioned part.\n\n Parameters:\n url: A complete urllib-parsed raw URL.\n\n Raises:\n VersionNotSupported: If the URL represents an OPTIMADE versioned base URL\n and the version part is not supported by the implementation.\n\n \"\"\"\n base_url = get_base_url(url)\n optimade_path = f\"{url.scheme}://{url.netloc}{url.path}\"[len(base_url) :]\n match = re.match(r\"^(?P<version>/v[0-9]+(\\.[0-9]+){0,2}).*\", optimade_path)\n if match is not None:\n if match.group(\"version\") not in BASE_URL_PREFIXES.values():\n raise VersionNotSupported(\n detail=(\n f\"The parsed versioned base URL {match.group('version')!r} from \"\n f\"{url} is not supported by this implementation. \"\n f\"Supported versioned base URLs are: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.EnsureQueryParamIntegrity","title":"EnsureQueryParamIntegrity
","text":" Bases: BaseHTTPMiddleware
Ensure all query parameters are followed by an equal sign (=
).
optimade/server/middleware.py
class EnsureQueryParamIntegrity(BaseHTTPMiddleware):\n \"\"\"Ensure all query parameters are followed by an equal sign (`=`).\"\"\"\n\n @staticmethod\n def check_url(url_query: str) -> set:\n \"\"\"Check parsed URL query part for parameters not followed by `=`.\n\n URL query parameters are considered to be split by ampersand (`&`)\n and semi-colon (`;`).\n\n Parameters:\n url_query: The raw urllib-parsed query part.\n\n Raises:\n BadRequest: If a query parameter does not come with a value.\n\n Returns:\n The set of individual query parameters and their values.\n\n This is mainly for testing and not actually neeeded by the middleware,\n since if the URL exhibits an invalid query part a `400 Bad Request`\n response will be returned.\n\n \"\"\"\n queries_amp = set(url_query.split(\"&\"))\n queries = set()\n for query in queries_amp:\n queries.update(set(query.split(\";\")))\n for query in queries:\n if \"=\" not in query and query != \"\":\n raise BadRequest(\n detail=\"A query parameter without an equal sign (=) is not supported by this server\"\n )\n return queries # Useful for testing\n\n async def dispatch(self, request: Request, call_next):\n parsed_url = urllib.parse.urlsplit(str(request.url))\n if parsed_url.query:\n self.check_url(parsed_url.query)\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.EnsureQueryParamIntegrity.check_url","title":"check_url(url_query)
staticmethod
","text":"Check parsed URL query part for parameters not followed by =
.
URL query parameters are considered to be split by ampersand (&
) and semi-colon (;
).
Parameters:
Name Type Description Defaulturl_query
str
The raw urllib-parsed query part.
requiredRaises:
Type DescriptionBadRequest
If a query parameter does not come with a value.
Returns:
Type Descriptionset
The set of individual query parameters and their values.
set
This is mainly for testing and not actually neeeded by the middleware,
set
since if the URL exhibits an invalid query part a 400 Bad Request
set
response will be returned.
Source code inoptimade/server/middleware.py
@staticmethod\ndef check_url(url_query: str) -> set:\n \"\"\"Check parsed URL query part for parameters not followed by `=`.\n\n URL query parameters are considered to be split by ampersand (`&`)\n and semi-colon (`;`).\n\n Parameters:\n url_query: The raw urllib-parsed query part.\n\n Raises:\n BadRequest: If a query parameter does not come with a value.\n\n Returns:\n The set of individual query parameters and their values.\n\n This is mainly for testing and not actually neeeded by the middleware,\n since if the URL exhibits an invalid query part a `400 Bad Request`\n response will be returned.\n\n \"\"\"\n queries_amp = set(url_query.split(\"&\"))\n queries = set()\n for query in queries_amp:\n queries.update(set(query.split(\";\")))\n for query in queries:\n if \"=\" not in query and query != \"\":\n raise BadRequest(\n detail=\"A query parameter without an equal sign (=) is not supported by this server\"\n )\n return queries # Useful for testing\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint","title":"HandleApiHint
","text":" Bases: BaseHTTPMiddleware
Handle api_hint
query parameter.
optimade/server/middleware.py
class HandleApiHint(BaseHTTPMiddleware):\n \"\"\"Handle `api_hint` query parameter.\"\"\"\n\n @staticmethod\n def handle_api_hint(api_hint: list[str]) -> Union[None, str]:\n \"\"\"Handle `api_hint` parameter value.\n\n There are several scenarios that can play out, when handling the `api_hint`\n query parameter:\n\n If several `api_hint` query parameters have been used, or a \"standard\" JSON\n list (`,`-separated value) has been supplied, a warning will be added to the\n response and the `api_hint` query parameter will not be applied.\n\n If the passed value does not comply with the rules set out in\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation),\n a warning will be added to the response and the `api_hint` query parameter\n will not be applied.\n\n If the value is part of the implementation's accepted versioned base URLs,\n it will be returned as is.\n\n If the value represents a major version that is newer than what is supported\n by the implementation, a `553 Version Not Supported` response will be returned,\n as is stated by [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation).\n\n On the other hand, if the value represents a major version equal to or lower\n than the implementation's supported major version, then the implementation's\n supported major version will be returned and tried for the request.\n\n Parameters:\n api_hint: The urllib-parsed query parameter value for `api_hint`.\n\n Raises:\n VersionNotSupported: If the requested major version is newer than the\n supported major version of the implementation.\n\n Returns:\n Either a valid `api_hint` value or `None`.\n\n \"\"\"\n # Try to split by `,` if value is provided once, but in JSON-type \"list\" format\n _api_hint = []\n for value in api_hint:\n values = value.split(\",\")\n _api_hint.extend(values)\n\n if len(_api_hint) > 1:\n warnings.warn(\n TooManyValues(\n detail=\"`api_hint` should only be supplied once, with a single value.\"\n )\n )\n return None\n\n api_hint_str: str = f\"/{_api_hint[0]}\"\n if re.match(r\"^/v[0-9]+(\\.[0-9]+)?$\", api_hint_str) is None:\n warnings.warn(\n FieldValueNotRecognized(\n detail=f\"{api_hint_str[1:]!r} is not recognized as a valid `api_hint` value.\"\n )\n )\n return None\n\n if api_hint_str in BASE_URL_PREFIXES.values():\n return api_hint_str\n\n major_api_hint = int(re.findall(r\"/v([0-9]+)\", api_hint_str)[0])\n major_implementation = int(BASE_URL_PREFIXES[\"major\"][len(\"/v\") :])\n\n if major_api_hint <= major_implementation:\n # If less than:\n # Use the current implementation in hope that it can still handle older requests\n #\n # If equal:\n # Go to /v<MAJOR>, since this should point to the latest available\n return BASE_URL_PREFIXES[\"major\"]\n\n # Let's not try to handle a request for a newer major version\n raise VersionNotSupported(\n detail=(\n f\"The provided `api_hint` ({api_hint_str[1:]!r}) is not supported by this implementation. \"\n f\"Supported versions include: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n\n @staticmethod\n def is_versioned_base_url(url: str) -> bool:\n \"\"\"Determine whether a request is for a versioned base URL.\n\n First, simply check whether a `/vMAJOR(.MINOR.PATCH)` part exists in the URL.\n If not, return `False`, else, remove unversioned base URL from the URL and check again.\n Return `bool` of final result.\n\n Parameters:\n url: The full URL to check.\n\n Returns:\n Whether or not the full URL represents an OPTIMADE versioned base URL.\n\n \"\"\"\n if not re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url):\n return False\n\n base_url = get_base_url(url)\n return bool(re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url[len(base_url) :]))\n\n async def dispatch(self, request: Request, call_next):\n parsed_query = urllib.parse.parse_qs(request.url.query, keep_blank_values=True)\n\n if \"api_hint\" in parsed_query:\n if self.is_versioned_base_url(str(request.url)):\n warnings.warn(\n QueryParamNotUsed(\n detail=(\n \"`api_hint` provided with value{:s} '{:s}' for a versioned base URL. \"\n \"In accordance with the specification, this will not be handled by \"\n \"the implementation.\".format(\n \"s\" if len(parsed_query[\"api_hint\"]) > 1 else \"\",\n \"', '\".join(parsed_query[\"api_hint\"]),\n )\n )\n )\n )\n else:\n from optimade.server.routers.utils import get_base_url\n\n version_path = self.handle_api_hint(parsed_query[\"api_hint\"])\n\n if version_path:\n base_url = get_base_url(request.url)\n\n new_request = (\n f\"{base_url}{version_path}{str(request.url)[len(base_url):]}\"\n )\n url = urllib.parse.urlsplit(new_request)\n q = \"&\".join(\n [\n f\"{key}={value}\"\n for key, value in urllib.parse.parse_qsl(\n url.query, keep_blank_values=True\n )\n if key != \"api_hint\"\n ]\n )\n\n return RedirectResponse(\n request.url.replace(path=url.path, query=q),\n headers=request.headers,\n )\n # This is the non-URL changing solution:\n #\n # scope = request.scope\n # scope[\"path\"] = path\n # request = Request(scope=scope, receive=request.receive, send=request._send)\n\n response = await call_next(request)\n return response\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint.handle_api_hint","title":"handle_api_hint(api_hint)
staticmethod
","text":"Handle api_hint
parameter value.
There are several scenarios that can play out, when handling the api_hint
query parameter:
If several api_hint
query parameters have been used, or a \"standard\" JSON list (,
-separated value) has been supplied, a warning will be added to the response and the api_hint
query parameter will not be applied.
If the passed value does not comply with the rules set out in the specification, a warning will be added to the response and the api_hint
query parameter will not be applied.
If the value is part of the implementation's accepted versioned base URLs, it will be returned as is.
If the value represents a major version that is newer than what is supported by the implementation, a 553 Version Not Supported
response will be returned, as is stated by the specification.
On the other hand, if the value represents a major version equal to or lower than the implementation's supported major version, then the implementation's supported major version will be returned and tried for the request.
Parameters:
Name Type Description Defaultapi_hint
list[str]
The urllib-parsed query parameter value for api_hint
.
Raises:
Type DescriptionVersionNotSupported
If the requested major version is newer than the supported major version of the implementation.
Returns:
Type DescriptionUnion[None, str]
Either a valid api_hint
value or None
.
optimade/server/middleware.py
@staticmethod\ndef handle_api_hint(api_hint: list[str]) -> Union[None, str]:\n \"\"\"Handle `api_hint` parameter value.\n\n There are several scenarios that can play out, when handling the `api_hint`\n query parameter:\n\n If several `api_hint` query parameters have been used, or a \"standard\" JSON\n list (`,`-separated value) has been supplied, a warning will be added to the\n response and the `api_hint` query parameter will not be applied.\n\n If the passed value does not comply with the rules set out in\n [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation),\n a warning will be added to the response and the `api_hint` query parameter\n will not be applied.\n\n If the value is part of the implementation's accepted versioned base URLs,\n it will be returned as is.\n\n If the value represents a major version that is newer than what is supported\n by the implementation, a `553 Version Not Supported` response will be returned,\n as is stated by [the specification](https://github.com/Materials-Consortia/OPTIMADE/blob/v1.0.0/optimade.rst#version-negotiation).\n\n On the other hand, if the value represents a major version equal to or lower\n than the implementation's supported major version, then the implementation's\n supported major version will be returned and tried for the request.\n\n Parameters:\n api_hint: The urllib-parsed query parameter value for `api_hint`.\n\n Raises:\n VersionNotSupported: If the requested major version is newer than the\n supported major version of the implementation.\n\n Returns:\n Either a valid `api_hint` value or `None`.\n\n \"\"\"\n # Try to split by `,` if value is provided once, but in JSON-type \"list\" format\n _api_hint = []\n for value in api_hint:\n values = value.split(\",\")\n _api_hint.extend(values)\n\n if len(_api_hint) > 1:\n warnings.warn(\n TooManyValues(\n detail=\"`api_hint` should only be supplied once, with a single value.\"\n )\n )\n return None\n\n api_hint_str: str = f\"/{_api_hint[0]}\"\n if re.match(r\"^/v[0-9]+(\\.[0-9]+)?$\", api_hint_str) is None:\n warnings.warn(\n FieldValueNotRecognized(\n detail=f\"{api_hint_str[1:]!r} is not recognized as a valid `api_hint` value.\"\n )\n )\n return None\n\n if api_hint_str in BASE_URL_PREFIXES.values():\n return api_hint_str\n\n major_api_hint = int(re.findall(r\"/v([0-9]+)\", api_hint_str)[0])\n major_implementation = int(BASE_URL_PREFIXES[\"major\"][len(\"/v\") :])\n\n if major_api_hint <= major_implementation:\n # If less than:\n # Use the current implementation in hope that it can still handle older requests\n #\n # If equal:\n # Go to /v<MAJOR>, since this should point to the latest available\n return BASE_URL_PREFIXES[\"major\"]\n\n # Let's not try to handle a request for a newer major version\n raise VersionNotSupported(\n detail=(\n f\"The provided `api_hint` ({api_hint_str[1:]!r}) is not supported by this implementation. \"\n f\"Supported versions include: {', '.join(BASE_URL_PREFIXES.values())}\"\n )\n )\n
"},{"location":"api_reference/server/middleware/#optimade.server.middleware.HandleApiHint.is_versioned_base_url","title":"is_versioned_base_url(url)
staticmethod
","text":"Determine whether a request is for a versioned base URL.
First, simply check whether a /vMAJOR(.MINOR.PATCH)
part exists in the URL. If not, return False
, else, remove unversioned base URL from the URL and check again. Return bool
of final result.
Parameters:
Name Type Description Defaulturl
str
The full URL to check.
requiredReturns:
Type Descriptionbool
Whether or not the full URL represents an OPTIMADE versioned base URL.
Source code inoptimade/server/middleware.py
@staticmethod\ndef is_versioned_base_url(url: str) -> bool:\n \"\"\"Determine whether a request is for a versioned base URL.\n\n First, simply check whether a `/vMAJOR(.MINOR.PATCH)` part exists in the URL.\n If not, return `False`, else, remove unversioned base URL from the URL and check again.\n Return `bool` of final result.\n\n Parameters:\n url: The full URL to check.\n\n Returns:\n Whether or not the full URL represents an OPTIMADE versioned base URL.\n\n \"\"\"\n if not re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url):\n return False\n\n base_url = get_base_url(url)\n return bool(re.findall(r\"(/v[0-9]+(\\.[0-9]+){0,2})\", url[len(base_url) :]))\n
"},{"location":"api_reference/server/query_params/","title":"query_params","text":""},{"location":"api_reference/server/query_params/#optimade.server.query_params.BaseQueryParams","title":"BaseQueryParams
","text":" Bases: ABC
A base class for query parameters that provides validation via the check_params
method.
Attributes:
Name Type Descriptionunsupported_params
list[str]
Any string parameter listed here will raise a warning when passed to the check_params methods. Useful for disabling optional OPTIMADE query parameters that are not implemented by the server, e.g., cursor-based pagination.
Source code inoptimade/server/query_params.py
class BaseQueryParams(ABC):\n \"\"\"A base class for query parameters that provides validation via the `check_params` method.\n\n Attributes:\n unsupported_params: Any string parameter listed here will raise a warning when passed to\n the check_params methods. Useful for disabling optional OPTIMADE query parameters that\n are not implemented by the server, e.g., cursor-based pagination.\n\n \"\"\"\n\n unsupported_params: list[str] = []\n\n def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.BaseQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.EntryListingQueryParams","title":"EntryListingQueryParams
","text":" Bases: BaseQueryParams
Common query params for all Entry listing endpoints.
Attributes:
Name Type Descriptionfilter
str
A filter string, in the format described in section API Filtering Format Specification of the specification.
response_format
str
The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification.
Example: http://example.com/v1/structures?response_format=xml
email_address
EmailStr
An email address of the user making the request. The email SHOULD be that of a person and not an automatic system.
Example: http://example.com/v1/structures?email_address=user@example.com
response_fields
str
A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present.
Example: http://example.com/v1/structures?response_fields=last_modified,nsites
sort
str
If supporting sortable queries, an implementation MUST use the sort
query parameter with format as specified by JSON API 1.0.
An implementation MAY support multiple sort fields for a single query. If it does, it again MUST conform to the JSON API 1.0 specification.
If an implementation supports sorting for an entry listing endpoint, then the /info/<entries>
endpoint MUST include, for each field name <fieldname>
in its data.properties.<fieldname>
response value that can be used for sorting, the key sortable
with value true
. If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the sortable
key or set it equal to false
for the specific field name. The set of field names, with sortable
equal to true
are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification. The field sortable
is in addition to each property description and other OPTIONAL fields. An example is shown in the section Entry Listing Info Endpoints.
page_limit
int
Sets a numerical limit on the number of entries returned. See JSON API 1.0. The API implementation MUST return no more than the number specified. It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden
-- MUST be returned). The default limit value is up to the API implementation to decide.
Example: http://example.com/optimade/v1/structures?page_limit=100
page_offset
int
RECOMMENDED for use with offset-based pagination: using page_offset
and page_limit
is RECOMMENDED.
Example: Skip 50 structures and fetch up to 100: /structures?page_offset=50&page_limit=100
.
page_number
int
RECOMMENDED for use with page-based pagination: using page_number
and page_limit
is RECOMMENDED. It is RECOMMENDED that the first page has number 1, i.e., that page_number
is 1-based.
Example: Fetch page 2 of up to 50 structures per page: /structures?page_number=2&page_limit=50
.
page_cursor
int
RECOMMENDED for use with cursor-based pagination: using page_cursor
and page_limit
is RECOMMENDED.
page_above
str
RECOMMENDED for use with value-based pagination: using page_above
/page_below
and page_limit
is RECOMMENDED.
Example: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing id
, so page_above
value refers to an id
value): /structures?page_above=4000&page_limit=100
.
page_below
str
RECOMMENDED for use with value-based pagination: using page_above
/page_below
and page_limit
is RECOMMENDED.
include
str
A server MAY implement the JSON API concept of returning compound documents by utilizing the include
query parameter as specified by JSON API 1.0.
All related resource objects MUST be returned as part of an array value for the top-level included
field, see the section JSON Response Schema: Common Fields.
The value of include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a 400 Bad Request
response MUST be made.
The default value for include
is references
. This means references
entries MUST always be included under the top-level field included
as default, since a server assumes if include
is not specified by a client in the request, it is still specified as include=references
. Note, if a client explicitly specifies include
and leaves out references
, references
resource objects MUST NOT be included under the top-level field included
, as per the definition of included
, see section JSON Response Schema: Common Fields.
Note: A query with the parameter include
set to the empty string means no related resource objects are to be returned under the top-level field included
.
api_hint
str
If the client provides the parameter, the value SHOULD have the format vMAJOR
or vMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends api_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.
optimade/server/query_params.py
class EntryListingQueryParams(BaseQueryParams):\n \"\"\"\n Common query params for all Entry listing endpoints.\n\n Attributes:\n filter (str): A filter string, in the format described in section API Filtering Format Specification of the specification.\n\n response_format (str): The output format requested (see section Response Format).\n Defaults to the format string 'json', which specifies the standard output format described in this specification.\n\n **Example**: `http://example.com/v1/structures?response_format=xml`\n\n email_address (EmailStr): An email address of the user making the request.\n The email SHOULD be that of a person and not an automatic system.\n\n **Example**: `http://example.com/v1/structures?email_address=user@example.com`\n\n response_fields (str): A comma-delimited set of fields to be provided in the output.\n If provided, these fields MUST be returned along with the REQUIRED fields.\n Other OPTIONAL fields MUST NOT be returned when this parameter is present.\n\n **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites`\n\n sort (str): If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified\n by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\n An implementation MAY support multiple sort fields for a single query.\n If it does, it again MUST conform to the JSON API 1.0 specification.\n\n If an implementation supports sorting for an entry listing endpoint, then the `/info/<entries>` endpoint MUST include,\n for each field name `<fieldname>` in its `data.properties.<fieldname>` response value that can be used for sorting,\n the key `sortable` with value `true`.\n If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either\n leave out the `sortable` key or set it equal to `false` for the specific field name.\n The set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to\n its definition in the JSON API 1.0 specification.\n The field `sortable` is in addition to each property description and other OPTIONAL fields.\n An example is shown in the section Entry Listing Info Endpoints.\n\n page_limit (int): Sets a numerical limit on the number of entries returned.\n See [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\n The API implementation MUST return no more than the number specified.\n It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers\n (in which case an error code -- `403 Forbidden` -- MUST be returned).\n The default limit value is up to the API implementation to decide.\n\n **Example**: `http://example.com/optimade/v1/structures?page_limit=100`\n\n page_offset (int): RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\n\n **Example**: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.\n\n page_number (int): RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\n It is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\n\n **Example**: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.\n\n page_cursor (int): RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.\n\n page_above (str): RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\n\n **Example**: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by\n increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.\n\n page_below (str): RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\n\n include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents)\n by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\n All related resource objects MUST be returned as part of an array value for the top-level `included` field,\n see the section JSON Response Schema: Common Fields.\n\n The value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\n If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\n The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field\n `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\n Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level\n field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.\n\n api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`,\n where MAJOR is a major version and MINOR is a minor version of the API.\n For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\n\n \"\"\"\n\n # The reference server implementation only supports offset/number-based pagination\n unsupported_params: list[str] = [\n \"page_cursor\",\n \"page_below\",\n ]\n\n def __init__(\n self,\n *,\n filter: Annotated[\n str,\n Query(\n description=\"A filter string, in the format described in section API Filtering Format Specification of the specification.\",\n ),\n ] = \"\",\n response_format: Annotated[\n str,\n Query(\n description=\"The output format requested (see section Response Format).\\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\\nExample: `http://example.com/v1/structures?response_format=xml`\",\n ),\n ] = \"json\",\n email_address: Annotated[\n Optional[EmailStr],\n Query(\n description=\"An email address of the user making the request.\\nThe email SHOULD be that of a person and not an automatic system.\\nExample: `http://example.com/v1/structures?email_address=user@example.com`\",\n ),\n ] = None,\n response_fields: Annotated[\n str,\n Query(\n description=\"A comma-delimited set of fields to be provided in the output.\\nIf provided, these fields MUST be returned along with the REQUIRED fields.\\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`\",\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n sort: Annotated[\n str,\n Query(\n description='If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\\n\\nAn implementation MAY support multiple sort fields for a single query.\\nIf it does, it again MUST conform to the JSON API 1.0 specification.\\n\\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/<entries>` endpoint MUST include, for each field name `<fieldname>` in its `data.properties.<fieldname>` response value that can be used for sorting, the key `sortable` with value `true`.\\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\\nAn example is shown in the section Entry Listing Info Endpoints.',\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n page_limit: Annotated[\n int,\n Query(\n description=\"Sets a numerical limit on the number of entries returned.\\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\\nThe API implementation MUST return no more than the number specified.\\nIt MAY return fewer.\\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\\nThe default limit value is up to the API implementation to decide.\\nExample: `http://example.com/optimade/v1/structures?page_limit=100`\",\n ge=0,\n ),\n ] = CONFIG.page_limit,\n page_offset: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.\",\n ge=0,\n ),\n ] = 0,\n page_number: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.\",\n # ge=1, # This constraint is only 'RECOMMENDED' in the specification, so should not be included here or in the OpenAPI schema\n ),\n ] = None, # type: ignore[assignment]\n page_cursor: Annotated[\n int,\n Query(\n description=\"RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.\",\n ge=0,\n ),\n ] = 0,\n page_above: Annotated[\n str,\n Query(\n description=\"RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.\",\n ),\n ] = None, # type: ignore[assignment]\n page_below: Annotated[\n str,\n Query(\n description=\"RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\",\n ),\n ] = None, # type: ignore[assignment]\n include: Annotated[\n str,\n Query(\n description='A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\\n\\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\\n\\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\\n\\nThe **default value** for `include` is `references`.\\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\\n\\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.',\n ),\n ] = \"references\",\n api_hint: Annotated[\n str,\n Query(\n description=\"If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\",\n pattern=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n ),\n ] = \"\",\n ):\n self.filter = filter\n self.response_format = response_format\n self.email_address = email_address\n self.response_fields = response_fields\n self.sort = sort\n self.page_limit = page_limit\n self.page_offset = page_offset\n self.page_number = page_number\n self.page_cursor = page_cursor\n self.page_above = page_above\n self.page_below = page_below\n self.include = include\n self.api_hint = api_hint\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.EntryListingQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.SingleEntryQueryParams","title":"SingleEntryQueryParams
","text":" Bases: BaseQueryParams
Common query params for single entry endpoints.
Attributes:
Name Type Descriptionresponse_format
str
The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification.
Example: http://example.com/v1/structures?response_format=xml
email_address
EmailStr
An email address of the user making the request. The email SHOULD be that of a person and not an automatic system.
Example: http://example.com/v1/structures?email_address=user@example.com
response_fields
str
A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present.
Example: http://example.com/v1/structures?response_fields=last_modified,nsites
include
str
A server MAY implement the JSON API concept of returning compound documents by utilizing the include
query parameter as specified by JSON API 1.0.
All related resource objects MUST be returned as part of an array value for the top-level included
field, see the section JSON Response Schema: Common Fields.
The value of include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a 400 Bad Request
response MUST be made.
The default value for include
is references
. This means references
entries MUST always be included under the top-level field included
as default, since a server assumes if include
is not specified by a client in the request, it is still specified as include=references
. Note, if a client explicitly specifies include
and leaves out references
, references
resource objects MUST NOT be included under the top-level field included
, as per the definition of included
, see section JSON Response Schema: Common Fields.
Note: A query with the parameter include
set to the empty string means no related resource objects are to be returned under the top-level field included
.
api_hint
str
If the client provides the parameter, the value SHOULD have the format vMAJOR
or vMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends api_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.
optimade/server/query_params.py
class SingleEntryQueryParams(BaseQueryParams):\n \"\"\"\n Common query params for single entry endpoints.\n\n Attributes:\n response_format (str): The output format requested (see section Response Format).\n Defaults to the format string 'json', which specifies the standard output format described in this specification.\n\n **Example**: `http://example.com/v1/structures?response_format=xml`\n\n email_address (EmailStr): An email address of the user making the request.\n The email SHOULD be that of a person and not an automatic system.\n\n **Example**: `http://example.com/v1/structures?email_address=user@example.com`\n\n response_fields (str): A comma-delimited set of fields to be provided in the output.\n If provided, these fields MUST be returned along with the REQUIRED fields.\n Other OPTIONAL fields MUST NOT be returned when this parameter is present.\n\n **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites`\n\n include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents)\n by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\n All related resource objects MUST be returned as part of an array value for the top-level `included` field,\n see the section JSON Response Schema: Common Fields.\n\n The value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\n If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\n The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field\n `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\n Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level\n field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.\n\n api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`,\n where MAJOR is a major version and MINOR is a minor version of the API.\n For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\n\n \"\"\"\n\n def __init__(\n self,\n *,\n response_format: Annotated[\n str,\n Query(\n description=\"The output format requested (see section Response Format).\\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\\nExample: `http://example.com/v1/structures?response_format=xml`\",\n ),\n ] = \"json\",\n email_address: Annotated[\n Optional[EmailStr],\n Query(\n description=\"An email address of the user making the request.\\nThe email SHOULD be that of a person and not an automatic system.\\nExample: `http://example.com/v1/structures?email_address=user@example.com`\",\n ),\n ] = None,\n response_fields: Annotated[\n str,\n Query(\n description=\"A comma-delimited set of fields to be provided in the output.\\nIf provided, these fields MUST be returned along with the REQUIRED fields.\\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`\",\n pattern=r\"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?\",\n ),\n ] = \"\",\n include: Annotated[\n str,\n Query(\n description='A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\\n\\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\\n\\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\\n\\nThe **default value** for `include` is `references`.\\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\\n\\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.',\n ),\n ] = \"references\",\n api_hint: Annotated[\n str,\n Query(\n description=\"If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.\",\n pattern=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n ),\n ] = \"\",\n ):\n self.response_format = response_format\n self.email_address = email_address\n self.response_fields = response_fields\n self.include = include\n self.api_hint = api_hint\n
"},{"location":"api_reference/server/query_params/#optimade.server.query_params.SingleEntryQueryParams.check_params","title":"check_params(query_params)
","text":"This method checks whether all the query parameters that are specified in the URL string are implemented in the relevant *QueryParams
class.
This method handles four cases:
*QueryParams
class, and it is not prefixed with a known provider prefix, then a BadRequest
is raised.*QueryParams
class, that is prefixed with a known provider prefix, then the parameter is silently ignored*QueryParams
class, that is prefixed with an unknown provider prefix, then a UnknownProviderQueryParameter
warning is emitted.unsupported_params
list for the inherited class, then a QueryParamNotUsed
warning is emitted.Parameters:
Name Type Description Defaultquery_params
Iterable[str]
An iterable of the request's string query parameters.
requiredRaises:
Type Description`BadRequest`
if the query parameter was not found in the relevant class, or if it does not have a valid prefix.
Source code inoptimade/server/query_params.py
def check_params(self, query_params: Iterable[str]) -> None:\n \"\"\"This method checks whether all the query parameters that are specified\n in the URL string are implemented in the relevant `*QueryParams` class.\n\n This method handles four cases:\n\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n and it is not prefixed with a known provider prefix, then a `BadRequest` is raised.\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with a known provider prefix, then the parameter is silently ignored\n * If a query parameter is passed that is not defined in the relevant `*QueryParams` class,\n that is prefixed with an unknown provider prefix, then a `UnknownProviderQueryParameter`\n warning is emitted.\n * If a query parameter is passed that is on the `unsupported_params` list for the inherited\n class, then a `QueryParamNotUsed` warning is emitted.\n\n Arguments:\n query_params: An iterable of the request's string query parameters.\n\n Raises:\n `BadRequest`: if the query parameter was not found in the relevant class, or if it\n does not have a valid prefix.\n\n \"\"\"\n if not getattr(CONFIG, \"validate_query_parameters\", False):\n return\n errors = []\n warnings = []\n unsupported_warnings = []\n for param in query_params:\n if param in self.unsupported_params:\n unsupported_warnings.append(param)\n if not hasattr(self, param):\n split_param = param.split(\"_\")\n if param.startswith(\"_\") and len(split_param) > 2:\n prefix = split_param[1]\n if prefix in BaseResourceMapper.SUPPORTED_PREFIXES:\n errors.append(param)\n elif prefix not in BaseResourceMapper.KNOWN_PROVIDER_PREFIXES:\n warnings.append(param)\n else:\n errors.append(param)\n\n if warnings:\n warn(\n f\"The query parameter(s) '{warnings}' are unrecognised and have been ignored.\",\n UnknownProviderQueryParameter,\n )\n\n if unsupported_warnings:\n warn(\n f\"The query parameter(s) '{unsupported_warnings}' are not supported by this server and have been ignored.\",\n QueryParamNotUsed,\n )\n\n if errors:\n raise BadRequest(\n f\"The query parameter(s) '{errors}' are not recognised by this endpoint.\"\n )\n
"},{"location":"api_reference/server/schemas/","title":"schemas","text":""},{"location":"api_reference/server/schemas/#optimade.server.schemas.ENTRY_INFO_SCHEMAS","title":"ENTRY_INFO_SCHEMAS: dict[str, type[EntryResource]] = {'structures': StructureResource, 'references': ReferenceResource}
module-attribute
","text":"This dictionary is used to define the /info/<entry_type>
endpoints.
retrieve_queryable_properties(schema, queryable_properties=None, entry_type=None)
","text":"Recursively loops through a pydantic model, returning a dictionary of all the OPTIMADE-queryable properties of that model.
Parameters:
Name Type Description Defaultschema
type[EntryResource]
The pydantic model.
requiredqueryable_properties
Optional[Iterable[str]]
The list of properties to find in the schema.
None
entry_type
Optional[str]
An optional entry type for the model. Will be used to lookup schemas for any config-defined fields.
None
Returns:
Type DescriptionQueryableProperties
A flat dictionary with properties as keys, containing the field
QueryableProperties
description, unit, sortability, support level, queryability
QueryableProperties
and type, where provided.
Source code inoptimade/server/schemas.py
def retrieve_queryable_properties(\n schema: type[EntryResource],\n queryable_properties: Optional[Iterable[str]] = None,\n entry_type: Optional[str] = None,\n) -> \"QueryableProperties\":\n \"\"\"Recursively loops through a pydantic model, returning a dictionary of all the\n OPTIMADE-queryable properties of that model.\n\n Parameters:\n schema: The pydantic model.\n queryable_properties: The list of properties to find in the schema.\n entry_type: An optional entry type for the model. Will be used to\n lookup schemas for any config-defined fields.\n\n Returns:\n A flat dictionary with properties as keys, containing the field\n description, unit, sortability, support level, queryability\n and type, where provided.\n\n \"\"\"\n properties: \"QueryableProperties\" = {}\n for name, value in schema.model_fields.items():\n # Proceed if the field (name) is given explicitly in the queryable_properties\n # list or if the queryable_properties list is empty (i.e., all properties are\n # requested)\n if not queryable_properties or name in queryable_properties:\n if name in properties:\n continue\n\n # If the field is another data model, \"unpack\" it by recursively calling\n # this function.\n # But first, we need to \"unpack\" the annotation, getting in behind any\n # Optional, Union, or Annotated types.\n annotation = _get_origin_type(value.annotation)\n\n if annotation not in (None, NoneType) and issubclass(annotation, BaseModel):\n sub_queryable_properties = list(annotation.model_fields) # type: ignore[attr-defined]\n properties.update(\n retrieve_queryable_properties(annotation, sub_queryable_properties)\n )\n\n properties[name] = {\"description\": value.description or \"\"}\n\n # Update schema with extension keys, provided they are not None\n for key in (\n \"x-optimade-unit\",\n \"x-optimade-queryable\",\n \"x-optimade-support\",\n ):\n if (\n value.json_schema_extra\n and value.json_schema_extra.get(key) is not None\n ):\n properties[name][\n key.replace(\"x-optimade-\", \"\") # type: ignore[index]\n ] = value.json_schema_extra[key]\n\n # All properties are sortable with the MongoDB backend.\n # While the result for sorting lists may not be as expected, they are still sorted.\n properties[name][\"sortable\"] = (\n value.json_schema_extra.get(\"x-optimade-sortable\", True)\n if value.json_schema_extra\n else True\n )\n\n # Try to get OpenAPI-specific \"format\" if possible, else get \"type\"; a mandatory OpenAPI key.\n json_schema = TypeAdapter(annotation).json_schema(mode=\"validation\")\n\n properties[name][\"type\"] = DataType.from_json_type(\n json_schema.get(\"format\", json_schema.get(\"type\"))\n )\n\n # If specified, check the config for any additional well-described provider fields\n if entry_type:\n from optimade.server.config import CONFIG\n\n described_provider_fields = [\n field\n for field in CONFIG.provider_fields.get(entry_type, {}) # type: ignore[call-overload]\n if isinstance(field, dict)\n ]\n for field in described_provider_fields:\n name = (\n f\"_{CONFIG.provider.prefix}_{field['name']}\"\n if not field[\"name\"].startswith(\"_\")\n else field[\"name\"]\n )\n properties[name] = {k: field[k] for k in field if k != \"name\"}\n properties[name][\"sortable\"] = field.get(\"sortable\", True)\n\n # Remove JSON fields mistaken as properties\n non_property_fields = [\"attributes\", \"relationships\"]\n for non_property_field in non_property_fields:\n if non_property_field in properties:\n del properties[non_property_field]\n\n return properties\n
"},{"location":"api_reference/server/warnings/","title":"warnings","text":"This submodule maintains backwards compatibility with the old optimade.server.warnings
module, which previously implemented the imported warnings directly.
FieldValueNotRecognized
","text":" Bases: OptimadeWarning
A field or value used in the request is not recognised by this implementation.
Source code inoptimade/warnings.py
class FieldValueNotRecognized(OptimadeWarning):\n \"\"\"A field or value used in the request is not recognised by this implementation.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.MissingExpectedField","title":"MissingExpectedField
","text":" Bases: LocalOptimadeWarning
A field was provided with a null value when a related field was provided with a value.
Source code inoptimade/warnings.py
class MissingExpectedField(LocalOptimadeWarning):\n \"\"\"A field was provided with a null value when a related field was provided\n with a value.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.OptimadeWarning","title":"OptimadeWarning
","text":" Bases: Warning
Base Warning for the optimade
package
optimade/warnings.py
class OptimadeWarning(Warning):\n \"\"\"Base Warning for the `optimade` package\"\"\"\n\n def __init__(\n self, detail: Optional[str] = None, title: Optional[str] = None, *args\n ) -> None:\n detail = detail if detail else self.__doc__\n super().__init__(detail, *args)\n self.detail = detail\n self.title = title if title else self.__class__.__name__\n\n def __repr__(self) -> str:\n attrs = {\"detail\": self.detail, \"title\": self.title}\n return \"<{:s}({:s})>\".format(\n self.__class__.__name__,\n \" \".join(\n [\n f\"{attr}={value!r}\"\n for attr, value in attrs.items()\n if value is not None\n ]\n ),\n )\n\n def __str__(self) -> str:\n return self.detail if self.detail is not None else \"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.QueryParamNotUsed","title":"QueryParamNotUsed
","text":" Bases: OptimadeWarning
A query parameter is not used in this request.
Source code inoptimade/warnings.py
class QueryParamNotUsed(OptimadeWarning):\n \"\"\"A query parameter is not used in this request.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.TimestampNotRFCCompliant","title":"TimestampNotRFCCompliant
","text":" Bases: OptimadeWarning
A timestamp has been used in a filter that contains microseconds and is thus not RFC 3339 compliant. This may cause undefined behaviour in the query results.
Source code inoptimade/warnings.py
class TimestampNotRFCCompliant(OptimadeWarning):\n \"\"\"A timestamp has been used in a filter that contains microseconds and is thus not\n RFC 3339 compliant. This may cause undefined behaviour in the query results.\n\n \"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.TooManyValues","title":"TooManyValues
","text":" Bases: OptimadeWarning
A field or query parameter has too many values to be handled by this implementation.
Source code inoptimade/warnings.py
class TooManyValues(OptimadeWarning):\n \"\"\"A field or query parameter has too many values to be handled by this implementation.\"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.UnknownProviderProperty","title":"UnknownProviderProperty
","text":" Bases: OptimadeWarning
A provider-specific property has been requested via response_fields
or as in a filter
that is not recognised by this implementation.
optimade/warnings.py
class UnknownProviderProperty(OptimadeWarning):\n \"\"\"A provider-specific property has been requested via `response_fields` or as in a `filter` that is not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/server/warnings/#optimade.server.warnings.UnknownProviderQueryParameter","title":"UnknownProviderQueryParameter
","text":" Bases: OptimadeWarning
A provider-specific query parameter has been requested in the query with a prefix not recognised by this implementation.
Source code inoptimade/warnings.py
class UnknownProviderQueryParameter(OptimadeWarning):\n \"\"\"A provider-specific query parameter has been requested in the query with a prefix not\n recognised by this implementation.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/","title":"elasticsearch","text":""},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection","title":"ElasticCollection
","text":" Bases: EntryCollection
optimade/server/entry_collections/elasticsearch.py
class ElasticCollection(EntryCollection):\n pagination_mechanism = PaginationMechanism(\"page_offset\")\n\n def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n client: Optional[\"Elasticsearch\"] = None,\n ):\n \"\"\"Initialize the ElasticCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n client: A preconfigured Elasticsearch client.\n\n \"\"\"\n super().__init__(\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n transformer=ElasticTransformer(mapper=resource_mapper),\n )\n\n self.client = client if client else CLIENT\n self.name = name\n\n # If we are creating a new collection from scratch, also create the index,\n # otherwise assume it has already been created externally\n if CONFIG.insert_test_data:\n self.create_optimade_index()\n\n def count(self, *args, **kwargs) -> int:\n raise NotImplementedError\n\n def create_optimade_index(self) -> None:\n \"\"\"Load or create an index that can handle aliased OPTIMADE fields and attach it\n to the current client.\n\n \"\"\"\n body = self.predefined_index.get(self.name)\n if body is None:\n body = self.create_elastic_index_from_mapper(\n self.resource_mapper, self.all_fields\n )\n\n properties = {}\n for field in list(body[\"mappings\"][\"properties\"].keys()):\n properties[self.resource_mapper.get_backend_field(field)] = body[\n \"mappings\"\n ][\"properties\"].pop(field)\n properties[\"id\"] = {\"type\": \"keyword\"}\n body[\"mappings\"][\"properties\"] = properties\n self.client.indices.create(index=self.name, ignore=400, **body)\n\n LOGGER.debug(f\"Created Elastic index for {self.name!r} with parameters {body}\")\n\n @property\n def predefined_index(self) -> dict[str, Any]:\n \"\"\"Loads and returns the default pre-defined index.\"\"\"\n with open(Path(__file__).parent.joinpath(\"elastic_indexes.json\")) as f:\n index = json.load(f)\n return index\n\n @staticmethod\n def create_elastic_index_from_mapper(\n resource_mapper: type[BaseResourceMapper], fields: Iterable[str]\n ) -> dict[str, Any]:\n \"\"\"Create a fallback elastic index based on a resource mapper.\n\n Arguments:\n resource_mapper: The resource mapper to create the index for.\n fields: The list of fields to use in the index.\n\n Returns:\n The parameters to pass to `client.indices.create(...)` (previously\n the 'body' parameters).\n\n \"\"\"\n properties = {\n resource_mapper.get_optimade_field(field): {\"type\": \"keyword\"}\n for field in fields\n }\n properties[\"id\"] = {\"type\": \"keyword\"}\n return {\"mappings\": {\"properties\": properties}}\n\n def __len__(self):\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return Search(using=self.client, index=self.name).execute().hits.total.value\n\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n def get_id(item):\n if self.name == \"links\":\n id_ = f\"{item['id']}-{item['type']}\"\n elif \"id\" in item:\n id_ = item[\"id\"]\n elif \"_id\" in item:\n # use the existing MongoDB ids in the test data\n id_ = str(item[\"_id\"])\n else:\n # ES will generate ids\n id_ = None\n item.pop(\"_id\", None)\n return id_\n\n bulk(\n self.client,\n (\n {\n \"_index\": self.name,\n \"_id\": get_id(item),\n \"_source\": item,\n }\n for item in data\n ),\n )\n\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry=False\n ) -> tuple[list[dict[str, Any]], int, bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n\n search = Search(using=self.client, index=self.name)\n\n if criteria.get(\"filter\", False):\n search = search.query(criteria[\"filter\"])\n\n page_offset = criteria.get(\"skip\", None)\n page_above = criteria.get(\"page_above\", None)\n\n limit = criteria.get(\"limit\", CONFIG.page_limit)\n\n all_aliased_fields = [\n self.resource_mapper.get_backend_field(field) for field in self.all_fields\n ]\n search = search.source(includes=all_aliased_fields)\n\n elastic_sort = [\n {field: {\"order\": \"desc\" if sort_dir == -1 else \"asc\"}}\n for field, sort_dir in criteria.get(\"sort\", {})\n ]\n if not elastic_sort:\n elastic_sort = [\n {self.resource_mapper.get_backend_field(\"id\"): {\"order\": \"asc\"}}\n ]\n\n search = search.sort(*elastic_sort)\n\n if page_offset:\n search = search[page_offset : page_offset + limit]\n\n elif page_above:\n search = search.extra(search_after=page_above, limit=limit)\n\n else:\n search = search[0:limit]\n page_offset = 0\n\n search = search.extra(track_total_hits=True)\n response = search.execute()\n\n results = [hit.to_dict() for hit in response.hits]\n\n more_data_available = False\n if not single_entry:\n data_returned = response.hits.total.value\n if page_above is not None:\n more_data_available = len(results) == limit and data_returned != limit\n else:\n more_data_available = page_offset + limit < data_returned\n else:\n # SingleEntryQueryParams, e.g., /structures/{entry_id}\n data_returned = len(results)\n\n return results, data_returned, more_data_available\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.predefined_index","title":"predefined_index: dict[str, Any]
property
","text":"Loads and returns the default pre-defined index.
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.__init__","title":"__init__(name, resource_cls, resource_mapper, client=None)
","text":"Initialize the ElasticCollection for the given parameters.
Parameters:
Name Type Description Defaultname
str
The name of the collection.
requiredresource_cls
type[EntryResource]
The type of entry resource that is stored by the collection.
requiredresource_mapper
type[BaseResourceMapper]
A resource mapper object that handles aliases and format changes between deserialization and response.
requiredclient
Optional[Elasticsearch]
A preconfigured Elasticsearch client.
None
Source code in optimade/server/entry_collections/elasticsearch.py
def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n client: Optional[\"Elasticsearch\"] = None,\n):\n \"\"\"Initialize the ElasticCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n client: A preconfigured Elasticsearch client.\n\n \"\"\"\n super().__init__(\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n transformer=ElasticTransformer(mapper=resource_mapper),\n )\n\n self.client = client if client else CLIENT\n self.name = name\n\n # If we are creating a new collection from scratch, also create the index,\n # otherwise assume it has already been created externally\n if CONFIG.insert_test_data:\n self.create_optimade_index()\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.__len__","title":"__len__()
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/elasticsearch.py
def __len__(self):\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return Search(using=self.client, index=self.name).execute().hits.total.value\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.create_elastic_index_from_mapper","title":"create_elastic_index_from_mapper(resource_mapper, fields)
staticmethod
","text":"Create a fallback elastic index based on a resource mapper.
Parameters:
Name Type Description Defaultresource_mapper
type[BaseResourceMapper]
The resource mapper to create the index for.
requiredfields
Iterable[str]
The list of fields to use in the index.
requiredReturns:
Type Descriptiondict[str, Any]
The parameters to pass to client.indices.create(...)
(previously the 'body' parameters).
optimade/server/entry_collections/elasticsearch.py
@staticmethod\ndef create_elastic_index_from_mapper(\n resource_mapper: type[BaseResourceMapper], fields: Iterable[str]\n) -> dict[str, Any]:\n \"\"\"Create a fallback elastic index based on a resource mapper.\n\n Arguments:\n resource_mapper: The resource mapper to create the index for.\n fields: The list of fields to use in the index.\n\n Returns:\n The parameters to pass to `client.indices.create(...)` (previously\n the 'body' parameters).\n\n \"\"\"\n properties = {\n resource_mapper.get_optimade_field(field): {\"type\": \"keyword\"}\n for field in fields\n }\n properties[\"id\"] = {\"type\": \"keyword\"}\n return {\"mappings\": {\"properties\": properties}}\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.create_optimade_index","title":"create_optimade_index()
","text":"Load or create an index that can handle aliased OPTIMADE fields and attach it to the current client.
Source code inoptimade/server/entry_collections/elasticsearch.py
def create_optimade_index(self) -> None:\n \"\"\"Load or create an index that can handle aliased OPTIMADE fields and attach it\n to the current client.\n\n \"\"\"\n body = self.predefined_index.get(self.name)\n if body is None:\n body = self.create_elastic_index_from_mapper(\n self.resource_mapper, self.all_fields\n )\n\n properties = {}\n for field in list(body[\"mappings\"][\"properties\"].keys()):\n properties[self.resource_mapper.get_backend_field(field)] = body[\n \"mappings\"\n ][\"properties\"].pop(field)\n properties[\"id\"] = {\"type\": \"keyword\"}\n body[\"mappings\"][\"properties\"] = properties\n self.client.indices.create(index=self.name, ignore=400, **body)\n\n LOGGER.debug(f\"Created Elastic index for {self.name!r} with parameters {body}\")\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend.
NoteCurrently this method returns the pymongo interpretation of the parameters, which will need modification for modified for other backends.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.insert","title":"insert(data)
","text":"Add the given entries to the underlying database.
WarningNo validation is performed on the incoming data.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/elasticsearch.py
def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n def get_id(item):\n if self.name == \"links\":\n id_ = f\"{item['id']}-{item['type']}\"\n elif \"id\" in item:\n id_ = item[\"id\"]\n elif \"_id\" in item:\n # use the existing MongoDB ids in the test data\n id_ = str(item[\"_id\"])\n else:\n # ES will generate ids\n id_ = None\n item.pop(\"_id\", None)\n return id_\n\n bulk(\n self.client,\n (\n {\n \"_index\": self.name,\n \"_id\": get_id(item),\n \"_source\": item,\n }\n for item in data\n ),\n )\n
"},{"location":"api_reference/server/entry_collections/elasticsearch/#optimade.server.entry_collections.elasticsearch.ElasticCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/entry_collections/entry_collections/","title":"entry_collections","text":""},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection","title":"EntryCollection
","text":" Bases: ABC
Backend-agnostic base class for querying collections of EntryResource
s.
optimade/server/entry_collections/entry_collections.py
class EntryCollection(ABC):\n \"\"\"Backend-agnostic base class for querying collections of\n [`EntryResource`][optimade.models.entries.EntryResource]s.\"\"\"\n\n pagination_mechanism = PaginationMechanism(\"page_offset\")\n \"\"\"The default pagination mechansim to use with a given collection,\n if the user does not provide any pagination query parameters.\n \"\"\"\n\n def __init__(\n self,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n transformer: Transformer,\n ):\n \"\"\"Initialize the collection for the given parameters.\n\n Parameters:\n resource_cls (EntryResource): The `EntryResource` model\n that is stored by the collection.\n resource_mapper (BaseResourceMapper): A resource mapper\n object that handles aliases and format changes between\n deserialization and response.\n transformer (Transformer): The Lark `Transformer` used to\n interpret the filter.\n\n \"\"\"\n self.parser = LarkParser()\n self.resource_cls = resource_cls\n self.resource_mapper = resource_mapper\n self.transformer = transformer\n\n self.provider_prefix = CONFIG.provider.prefix\n self.provider_fields = [\n field if isinstance(field, str) else field[\"name\"]\n for field in CONFIG.provider_fields.get(resource_mapper.ENDPOINT, [])\n ]\n\n self._all_fields: set[str] = set()\n\n @abstractmethod\n def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n\n @abstractmethod\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n\n @abstractmethod\n def count(self, **kwargs: Any) -> Optional[int]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments.\n\n \"\"\"\n\n def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n ]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n\n @abstractmethod\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry: bool = False\n ) -> tuple[list[dict[str, Any]], Optional[int], bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n\n @property\n def all_fields(self) -> set[str]:\n \"\"\"Get the set of all fields handled in this collection,\n from attribute fields in the schema, provider fields and top-level OPTIMADE fields.\n\n The set of all fields are lazily created and then cached.\n This means the set is created the first time the property is requested and then cached.\n\n Returns:\n All fields handled in this collection.\n\n \"\"\"\n if not self._all_fields:\n # All OPTIMADE fields\n self._all_fields = (\n self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS.copy()\n )\n self._all_fields |= self.get_attribute_fields()\n # All provider-specific fields\n self._all_fields |= {\n f\"_{self.provider_prefix}_{field_name}\"\n if not field_name.startswith(\"_\")\n else field_name\n for field_name in self.provider_fields\n }\n\n return self._all_fields\n\n def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n\n def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n\n def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n\n def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n ) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.pagination_mechanism","title":"pagination_mechanism = PaginationMechanism('page_offset')
class-attribute
instance-attribute
","text":"The default pagination mechansim to use with a given collection, if the user does not provide any pagination query parameters.
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.__init__","title":"__init__(resource_cls, resource_mapper, transformer)
","text":"Initialize the collection for the given parameters.
Parameters:
Name Type Description Defaultresource_cls
EntryResource
The EntryResource
model that is stored by the collection.
resource_mapper
BaseResourceMapper
A resource mapper object that handles aliases and format changes between deserialization and response.
requiredtransformer
Transformer
The Lark Transformer
used to interpret the filter.
optimade/server/entry_collections/entry_collections.py
def __init__(\n self,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n transformer: Transformer,\n):\n \"\"\"Initialize the collection for the given parameters.\n\n Parameters:\n resource_cls (EntryResource): The `EntryResource` model\n that is stored by the collection.\n resource_mapper (BaseResourceMapper): A resource mapper\n object that handles aliases and format changes between\n deserialization and response.\n transformer (Transformer): The Lark `Transformer` used to\n interpret the filter.\n\n \"\"\"\n self.parser = LarkParser()\n self.resource_cls = resource_cls\n self.resource_mapper = resource_mapper\n self.transformer = transformer\n\n self.provider_prefix = CONFIG.provider.prefix\n self.provider_fields = [\n field if isinstance(field, str) else field[\"name\"]\n for field in CONFIG.provider_fields.get(resource_mapper.ENDPOINT, [])\n ]\n\n self._all_fields: set[str] = set()\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.__len__","title":"__len__()
abstractmethod
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.count","title":"count(**kwargs)
abstractmethod
","text":"Returns the number of entries matching the query specified by the keyword arguments.
Parameters:
Name Type Description Default**kwargs
Any
Query parameters as keyword arguments.
{}
Source code in optimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef count(self, **kwargs: Any) -> Optional[int]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend.
NoteCurrently this method returns the pymongo interpretation of the parameters, which will need modification for modified for other backends.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by the specific backend.\n\n Note:\n Currently this method returns the pymongo interpretation of the parameters,\n which will need modification for modified for other backends.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n cursor_kwargs = {}\n\n # filter\n if getattr(params, \"filter\", False):\n cursor_kwargs[\"filter\"] = self.transformer.transform(\n self.parser.parse(params.filter) # type: ignore[union-attr]\n )\n else:\n cursor_kwargs[\"filter\"] = {}\n\n # response_format\n if (\n getattr(params, \"response_format\", False)\n and params.response_format != \"json\"\n ):\n raise BadRequest(\n detail=f\"Response format {params.response_format} is not supported, please use response_format='json'\"\n )\n\n # page_limit\n if getattr(params, \"page_limit\", False):\n limit = params.page_limit # type: ignore[union-attr]\n if limit > CONFIG.page_limit_max:\n raise Forbidden(\n detail=f\"Max allowed page_limit is {CONFIG.page_limit_max}, you requested {limit}\",\n )\n cursor_kwargs[\"limit\"] = limit\n else:\n cursor_kwargs[\"limit\"] = CONFIG.page_limit\n\n # response_fields\n cursor_kwargs[\"projection\"] = {\n f\"{self.resource_mapper.get_backend_field(f)}\": True\n for f in self.all_fields\n }\n\n if getattr(params, \"response_fields\", False):\n response_fields = set(params.response_fields.split(\",\"))\n response_fields |= self.resource_mapper.get_required_fields()\n else:\n response_fields = self.all_fields.copy()\n\n cursor_kwargs[\"fields\"] = response_fields\n\n # sort\n if getattr(params, \"sort\", False):\n cursor_kwargs[\"sort\"] = self.parse_sort_params(params.sort) # type: ignore[union-attr]\n\n # warn if multiple pagination keys are present, and only use the first from this list\n received_pagination_option = False\n warn_multiple_keys = False\n\n if getattr(params, \"page_offset\", False):\n received_pagination_option = True\n cursor_kwargs[\"skip\"] = params.page_offset # type: ignore[union-attr]\n\n if isinstance(getattr(params, \"page_number\", None), int):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n if params.page_number < 1: # type: ignore[union-attr]\n warnings.warn(\n message=f\"'page_number' is 1-based, using 'page_number=1' instead of {params.page_number}\", # type: ignore[union-attr]\n category=QueryParamNotUsed,\n )\n page_number = 1\n else:\n page_number = params.page_number # type: ignore[union-attr]\n cursor_kwargs[\"skip\"] = (page_number - 1) * cursor_kwargs[\"limit\"]\n\n if isinstance(getattr(params, \"page_above\", None), str):\n if received_pagination_option:\n warn_multiple_keys = True\n else:\n received_pagination_option = True\n cursor_kwargs[\"page_above\"] = params.page_above # type: ignore[union-attr]\n\n if warn_multiple_keys:\n warnings.warn(\n message=\"Multiple pagination keys were provided, only using the first one of 'page_offset', 'page_number' or 'page_above'\",\n category=QueryParamNotUsed,\n )\n\n return cursor_kwargs\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.insert","title":"insert(data)
abstractmethod
","text":"Add the given entries to the underlying database.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/entry_collections.py
@abstractmethod\ndef insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.EntryCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.PaginationMechanism","title":"PaginationMechanism
","text":" Bases: Enum
The supported pagination mechanisms.
Source code inoptimade/server/entry_collections/entry_collections.py
class PaginationMechanism(enum.Enum):\n \"\"\"The supported pagination mechanisms.\"\"\"\n\n OFFSET = \"page_offset\"\n NUMBER = \"page_number\"\n CURSOR = \"page_cursor\"\n ABOVE = \"page_above\"\n BELOW = \"page_below\"\n
"},{"location":"api_reference/server/entry_collections/entry_collections/#optimade.server.entry_collections.entry_collections.create_collection","title":"create_collection(name, resource_cls, resource_mapper)
","text":"Create an EntryCollection
of the configured type, depending on the value of CONFIG.database_backend
.
Parameters:
Name Type Description Defaultname
str
The collection name.
requiredresource_cls
type[EntryResource]
The type of entry resource to be stored within the collection.
requiredresource_mapper
type[BaseResourceMapper]
The associated resource mapper for that entry resource type.
requiredReturns:
Type DescriptionEntryCollection
The created EntryCollection
.
optimade/server/entry_collections/entry_collections.py
def create_collection(\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n) -> \"EntryCollection\":\n \"\"\"Create an `EntryCollection` of the configured type, depending on the value of\n `CONFIG.database_backend`.\n\n Arguments:\n name: The collection name.\n resource_cls: The type of entry resource to be stored within the collection.\n resource_mapper: The associated resource mapper for that entry resource type.\n\n Returns:\n The created `EntryCollection`.\n\n \"\"\"\n if CONFIG.database_backend in (\n SupportedBackend.MONGODB,\n SupportedBackend.MONGOMOCK,\n ):\n from optimade.server.entry_collections.mongo import MongoCollection\n\n return MongoCollection(\n name=name,\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n )\n\n if CONFIG.database_backend is SupportedBackend.ELASTIC:\n from optimade.server.entry_collections.elasticsearch import ElasticCollection\n\n return ElasticCollection(\n name=name,\n resource_cls=resource_cls,\n resource_mapper=resource_mapper,\n )\n\n raise NotImplementedError(\n f\"The database backend {CONFIG.database_backend!r} is not implemented\"\n )\n
"},{"location":"api_reference/server/entry_collections/mongo/","title":"mongo","text":""},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection","title":"MongoCollection
","text":" Bases: EntryCollection
Class for querying MongoDB collections (implemented by either pymongo or mongomock) containing serialized EntryResource
s objects.
optimade/server/entry_collections/mongo.py
class MongoCollection(EntryCollection):\n \"\"\"Class for querying MongoDB collections (implemented by either pymongo or mongomock)\n containing serialized [`EntryResource`][optimade.models.entries.EntryResource]s objects.\n\n \"\"\"\n\n def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n database: str = CONFIG.mongo_database,\n ):\n \"\"\"Initialize the MongoCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n database: The name of the underlying MongoDB database to connect to.\n\n \"\"\"\n super().__init__(\n resource_cls,\n resource_mapper,\n MongoTransformer(mapper=resource_mapper),\n )\n\n self.collection = CLIENT[database][name]\n\n # check aliases do not clash with mongo operators\n self._check_aliases(self.resource_mapper.all_aliases())\n self._check_aliases(self.resource_mapper.all_length_aliases())\n\n def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return self.collection.estimated_document_count()\n\n def count(self, **kwargs: Any) -> Union[int, None]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments, or `None` if the count timed out.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments. The keys\n 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed\n to the `pymongo.collection.Collection.count_documents` method.\n\n \"\"\"\n for k in list(kwargs.keys()):\n if k not in (\"filter\", \"skip\", \"limit\", \"hint\", \"maxTimeMS\"):\n del kwargs[k]\n if \"filter\" not in kwargs:\n return self.collection.estimated_document_count()\n else:\n if \"maxTimeMS\" not in kwargs:\n kwargs[\"maxTimeMS\"] = 1000 * CONFIG.mongo_count_timeout\n try:\n return self.collection.count_documents(**kwargs)\n except ExecutionTimeout:\n return None\n\n def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n self.collection.insert_many(data)\n\n def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n ) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by MongoDB.\n\n This Mongo-specific method calls the base `EntryCollection.handle_query_params` method\n and adds additional handling of the MongoDB ObjectID type.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n criteria = super().handle_query_params(params)\n # Handle MongoDB ObjectIDs:\n # - If they were not requested, then explicitly remove them\n # - If they were requested, then cast them to strings in the response\n if \"_id\" not in criteria.get(\"projection\", {}):\n criteria[\"projection\"][\"_id\"] = False\n\n if \"page_above\" in criteria:\n raise NotImplementedError(\n \"`page_above` is not implemented for this backend.\"\n )\n\n if criteria.get(\"projection\", {}).get(\"_id\"):\n criteria[\"projection\"][\"_id\"] = {\"$toString\": \"$_id\"}\n\n return criteria\n\n def _run_db_query(\n self, criteria: dict[str, Any], single_entry: bool = False\n ) -> tuple[list[dict[str, Any]], Optional[int], bool]:\n \"\"\"Run the query on the backend and collect the results.\n\n Arguments:\n criteria: A dictionary representation of the query parameters.\n single_entry: Whether or not the caller is expecting a single entry response.\n\n Returns:\n The list of entries from the database (without any re-mapping), the total number of\n entries matching the query and a boolean for whether or not there is more data available.\n\n \"\"\"\n results = list(self.collection.find(**criteria))\n\n if CONFIG.database_backend == SupportedBackend.MONGOMOCK and criteria.get(\n \"projection\", {}\n ).get(\"_id\"):\n # mongomock does not support `$toString`` in projection, so we have to do it manually\n for ind, doc in enumerate(results):\n results[ind][\"_id\"] = str(doc[\"_id\"])\n\n nresults_now = len(results)\n if not single_entry:\n criteria_nolimit = criteria.copy()\n criteria_nolimit.pop(\"limit\", None)\n skip = criteria_nolimit.pop(\"skip\", 0)\n data_returned = self.count(**criteria_nolimit)\n # Only correct most of the time: if the total number of remaining results is exactly the page limit\n # then this will incorrectly say there is more_data_available\n if data_returned is None:\n more_data_available = nresults_now == criteria.get(\"limit\", 0)\n else:\n more_data_available = nresults_now + skip < data_returned\n else:\n # SingleEntryQueryParams, e.g., /structures/{entry_id}\n data_returned = nresults_now\n more_data_available = False\n\n return results, data_returned, more_data_available\n\n def _check_aliases(self, aliases):\n \"\"\"Check that aliases do not clash with mongo keywords.\"\"\"\n if any(\n alias[0].startswith(\"$\") or alias[1].startswith(\"$\") for alias in aliases\n ):\n raise RuntimeError(f\"Cannot define an alias starting with a '$': {aliases}\")\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.all_fields","title":"all_fields: set[str]
property
","text":"Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields.
The set of all fields are lazily created and then cached. This means the set is created the first time the property is requested and then cached.
Returns:
Type Descriptionset[str]
All fields handled in this collection.
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.pagination_mechanism","title":"pagination_mechanism = PaginationMechanism('page_offset')
class-attribute
instance-attribute
","text":"The default pagination mechansim to use with a given collection, if the user does not provide any pagination query parameters.
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.__init__","title":"__init__(name, resource_cls, resource_mapper, database=CONFIG.mongo_database)
","text":"Initialize the MongoCollection for the given parameters.
Parameters:
Name Type Description Defaultname
str
The name of the collection.
requiredresource_cls
type[EntryResource]
The type of entry resource that is stored by the collection.
requiredresource_mapper
type[BaseResourceMapper]
A resource mapper object that handles aliases and format changes between deserialization and response.
requireddatabase
str
The name of the underlying MongoDB database to connect to.
mongo_database
Source code in optimade/server/entry_collections/mongo.py
def __init__(\n self,\n name: str,\n resource_cls: type[EntryResource],\n resource_mapper: type[BaseResourceMapper],\n database: str = CONFIG.mongo_database,\n):\n \"\"\"Initialize the MongoCollection for the given parameters.\n\n Parameters:\n name: The name of the collection.\n resource_cls: The type of entry resource that is stored by the collection.\n resource_mapper: A resource mapper object that handles aliases and\n format changes between deserialization and response.\n database: The name of the underlying MongoDB database to connect to.\n\n \"\"\"\n super().__init__(\n resource_cls,\n resource_mapper,\n MongoTransformer(mapper=resource_mapper),\n )\n\n self.collection = CLIENT[database][name]\n\n # check aliases do not clash with mongo operators\n self._check_aliases(self.resource_mapper.all_aliases())\n self._check_aliases(self.resource_mapper.all_length_aliases())\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.__len__","title":"__len__()
","text":"Returns the total number of entries in the collection.
Source code inoptimade/server/entry_collections/mongo.py
def __len__(self) -> int:\n \"\"\"Returns the total number of entries in the collection.\"\"\"\n return self.collection.estimated_document_count()\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.count","title":"count(**kwargs)
","text":"Returns the number of entries matching the query specified by the keyword arguments, or None
if the count timed out.
Parameters:
Name Type Description Default**kwargs
Any
Query parameters as keyword arguments. The keys 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed to the pymongo.collection.Collection.count_documents
method.
{}
Source code in optimade/server/entry_collections/mongo.py
def count(self, **kwargs: Any) -> Union[int, None]:\n \"\"\"Returns the number of entries matching the query specified\n by the keyword arguments, or `None` if the count timed out.\n\n Parameters:\n **kwargs: Query parameters as keyword arguments. The keys\n 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed\n to the `pymongo.collection.Collection.count_documents` method.\n\n \"\"\"\n for k in list(kwargs.keys()):\n if k not in (\"filter\", \"skip\", \"limit\", \"hint\", \"maxTimeMS\"):\n del kwargs[k]\n if \"filter\" not in kwargs:\n return self.collection.estimated_document_count()\n else:\n if \"maxTimeMS\" not in kwargs:\n kwargs[\"maxTimeMS\"] = 1000 * CONFIG.mongo_count_timeout\n try:\n return self.collection.count_documents(**kwargs)\n except ExecutionTimeout:\n return None\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.find","title":"find(params)
","text":"Fetches results and indicates if more data is available.
Also gives the total number of data available in the absence of page_limit
. See EntryListingQueryParams
for more information.
Returns a list of the mapped database reponse.
If no results match the query, then results
is set to None
.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
Entry listing URL query params.
requiredReturns:
Type DescriptionOptional[Union[dict[str, Any], list[dict[str, Any]]]]
A tuple of various relevant values:
Optional[int]
(results
, data_returned
, more_data_available
, exclude_fields
, include_fields
).
optimade/server/entry_collections/entry_collections.py
def find(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> tuple[\n Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n Optional[int],\n bool,\n set[str],\n set[str],\n]:\n \"\"\"\n Fetches results and indicates if more data is available.\n\n Also gives the total number of data available in the absence of `page_limit`.\n See [`EntryListingQueryParams`][optimade.server.query_params.EntryListingQueryParams]\n for more information.\n\n Returns a list of the mapped database reponse.\n\n If no results match the query, then `results` is set to `None`.\n\n Parameters:\n params: Entry listing URL query params.\n\n Returns:\n A tuple of various relevant values:\n (`results`, `data_returned`, `more_data_available`, `exclude_fields`, `include_fields`).\n\n \"\"\"\n criteria = self.handle_query_params(params)\n single_entry = isinstance(params, SingleEntryQueryParams)\n response_fields: set[str] = criteria.pop(\"fields\")\n\n raw_results, data_returned, more_data_available = self._run_db_query(\n criteria, single_entry\n )\n\n exclude_fields = self.all_fields - response_fields\n include_fields = (\n response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n )\n\n bad_optimade_fields: set[str] = set()\n bad_provider_fields: set[str] = set()\n supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES\n all_attributes: set[str] = self.resource_mapper.ALL_ATTRIBUTES\n for field in include_fields:\n if field not in all_attributes:\n if field.startswith(\"_\"):\n if any(\n field.startswith(f\"_{prefix}_\") for prefix in supported_prefixes\n ):\n bad_provider_fields.add(field)\n else:\n bad_optimade_fields.add(field)\n\n if bad_provider_fields:\n warnings.warn(\n message=f\"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.\",\n category=UnknownProviderProperty,\n )\n\n if bad_optimade_fields:\n raise BadRequest(\n detail=f\"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}.\"\n )\n\n results: Optional[Union[list[dict[str, Any]], dict[str, Any]]] = None\n\n if raw_results:\n results = [self.resource_mapper.map_back(doc) for doc in raw_results]\n\n if single_entry:\n results = results[0]\n\n if (\n CONFIG.validate_api_response\n and data_returned is not None\n and data_returned > 1\n ):\n raise NotFound(\n detail=f\"Instead of a single entry, {data_returned} entries were found\",\n )\n else:\n data_returned = 1\n\n return (\n results,\n data_returned,\n more_data_available,\n exclude_fields,\n include_fields,\n )\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.get_attribute_fields","title":"get_attribute_fields()
","text":"Get the set of attribute fields
Return only the first-level attribute fields from the schema of the resource class, resolving references along the way if needed.
NoteIt is not needed to take care of other special OpenAPI schema keys than allOf
, since only allOf
will be found in this context. Other special keys can be found in the Swagger documentation.
Returns:
Type Descriptionset[str]
Property names.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_attribute_fields(self) -> set[str]:\n \"\"\"Get the set of attribute fields\n\n Return only the _first-level_ attribute fields from the schema of the resource class,\n resolving references along the way if needed.\n\n Note:\n It is not needed to take care of other special OpenAPI schema keys than `allOf`,\n since only `allOf` will be found in this context.\n Other special keys can be found in [the Swagger documentation](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/).\n\n Returns:\n Property names.\n\n \"\"\"\n annotation = _get_origin_type(\n self.resource_cls.model_fields[\"attributes\"].annotation\n )\n\n if annotation in (None, NoneType) or not issubclass(annotation, Attributes):\n raise TypeError(\n \"resource class 'attributes' field must be a subclass of 'EntryResourceAttributes'\"\n )\n\n return set(annotation.model_fields) # type: ignore[attr-defined]\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.get_next_query_params","title":"get_next_query_params(params, results)
","text":"Provides url query pagination parameters that will be used in the next link.
Parameters:
Name Type Description Defaultresults
Optional[Union[dict[str, Any], list[dict[str, Any]]]]
The results produced by find.
requiredparams
EntryListingQueryParams
The parsed request params produced by handle_query_params.
requiredReturns:
Type Descriptiondict[str, list[str]]
A dictionary with the necessary query parameters.
Source code inoptimade/server/entry_collections/entry_collections.py
def get_next_query_params(\n self,\n params: EntryListingQueryParams,\n results: Optional[Union[dict[str, Any], list[dict[str, Any]]]],\n) -> dict[str, list[str]]:\n \"\"\"Provides url query pagination parameters that will be used in the next\n link.\n\n Arguments:\n results: The results produced by find.\n params: The parsed request params produced by handle_query_params.\n\n Returns:\n A dictionary with the necessary query parameters.\n\n \"\"\"\n query: dict[str, list[str]] = dict()\n if isinstance(results, list) and results:\n # If a user passed a particular pagination mechanism, keep using it\n # Otherwise, use the default pagination mechanism of the collection\n pagination_mechanism = PaginationMechanism.OFFSET\n for pagination_key in (\n \"page_offset\",\n \"page_number\",\n \"page_above\",\n ):\n if getattr(params, pagination_key, None) is not None:\n pagination_mechanism = PaginationMechanism(pagination_key)\n break\n\n if pagination_mechanism == PaginationMechanism.OFFSET:\n query[\"page_offset\"] = [\n str(params.page_offset + len(results)) # type: ignore[list-item]\n ]\n\n return query\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.handle_query_params","title":"handle_query_params(params)
","text":"Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB.
This Mongo-specific method calls the base EntryCollection.handle_query_params
method and adds additional handling of the MongoDB ObjectID type.
Parameters:
Name Type Description Defaultparams
Union[EntryListingQueryParams, SingleEntryQueryParams]
The initialized query parameter model from the server.
requiredRaises:
Type DescriptionForbidden
If too large of a page limit is provided.
BadRequest
If an invalid request is made, e.g., with incorrect fields or response format.
Returns:
Type Descriptiondict[str, Any]
A dictionary representation of the query parameters.
Source code inoptimade/server/entry_collections/mongo.py
def handle_query_params(\n self, params: Union[EntryListingQueryParams, SingleEntryQueryParams]\n) -> dict[str, Any]:\n \"\"\"Parse and interpret the backend-agnostic query parameter models into a dictionary\n that can be used by MongoDB.\n\n This Mongo-specific method calls the base `EntryCollection.handle_query_params` method\n and adds additional handling of the MongoDB ObjectID type.\n\n Parameters:\n params: The initialized query parameter model from the server.\n\n Raises:\n Forbidden: If too large of a page limit is provided.\n BadRequest: If an invalid request is made, e.g., with incorrect fields\n or response format.\n\n Returns:\n A dictionary representation of the query parameters.\n\n \"\"\"\n criteria = super().handle_query_params(params)\n # Handle MongoDB ObjectIDs:\n # - If they were not requested, then explicitly remove them\n # - If they were requested, then cast them to strings in the response\n if \"_id\" not in criteria.get(\"projection\", {}):\n criteria[\"projection\"][\"_id\"] = False\n\n if \"page_above\" in criteria:\n raise NotImplementedError(\n \"`page_above` is not implemented for this backend.\"\n )\n\n if criteria.get(\"projection\", {}).get(\"_id\"):\n criteria[\"projection\"][\"_id\"] = {\"$toString\": \"$_id\"}\n\n return criteria\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.insert","title":"insert(data)
","text":"Add the given entries to the underlying database.
WarningNo validation is performed on the incoming data.
Parameters:
Name Type Description Defaultdata
list[EntryResource]
The entry resource objects to add to the database.
required Source code inoptimade/server/entry_collections/mongo.py
def insert(self, data: list[EntryResource]) -> None:\n \"\"\"Add the given entries to the underlying database.\n\n Warning:\n No validation is performed on the incoming data.\n\n Arguments:\n data: The entry resource objects to add to the database.\n\n \"\"\"\n self.collection.insert_many(data)\n
"},{"location":"api_reference/server/entry_collections/mongo/#optimade.server.entry_collections.mongo.MongoCollection.parse_sort_params","title":"parse_sort_params(sort_params)
","text":"Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields.
Raises:
Type DescriptionBadRequest
if an invalid sort is requested.
Returns:
Type DescriptionIterable[tuple[str, int]]
A list of tuples containing the aliased field name and
Iterable[tuple[str, int]]
sort direction encoded as 1 (ascending) or -1 (descending).
Source code inoptimade/server/entry_collections/entry_collections.py
def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]:\n \"\"\"Handles any sort parameters passed to the collection,\n resolving aliases and dealing with any invalid fields.\n\n Raises:\n BadRequest: if an invalid sort is requested.\n\n Returns:\n A list of tuples containing the aliased field name and\n sort direction encoded as 1 (ascending) or -1 (descending).\n\n \"\"\"\n sort_spec: list[tuple[str, int]] = []\n for field in sort_params.split(\",\"):\n sort_dir = 1\n if field.startswith(\"-\"):\n field = field[1:]\n sort_dir = -1\n aliased_field = self.resource_mapper.get_backend_field(field)\n sort_spec.append((aliased_field, sort_dir))\n\n unknown_fields = [\n field\n for field, _ in sort_spec\n if self.resource_mapper.get_optimade_field(field) not in self.all_fields\n ]\n\n if unknown_fields:\n error_detail = \"Unable to sort on unknown field{} '{}'\".format(\n \"s\" if len(unknown_fields) > 1 else \"\",\n \"', '\".join(unknown_fields),\n )\n\n # If all unknown fields are \"other\" provider-specific, then only provide a warning\n if all(\n (\n re.match(r\"_[a-z_0-9]+_[a-z_0-9]*\", field)\n and not field.startswith(f\"_{self.provider_prefix}_\")\n )\n for field in unknown_fields\n ):\n warnings.warn(error_detail, FieldValueNotRecognized)\n\n # Otherwise, if all fields are unknown, or some fields are unknown and do not\n # have other provider prefixes, then return 400: Bad Request\n else:\n raise BadRequest(detail=error_detail)\n\n # If at least one valid field has been provided for sorting, then use that\n sort_spec = [\n (field, sort_dir)\n for field, sort_dir in sort_spec\n if field not in unknown_fields\n ]\n\n return sort_spec\n
"},{"location":"api_reference/server/mappers/entries/","title":"entries","text":""},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper","title":"BaseResourceMapper
","text":"Generic Resource Mapper that defines and performs the mapping between objects in the database and the resource objects defined by the specification.
Attributes:
Name Type DescriptionALIASES
tuple[tuple[str, str], ...]
a tuple of aliases between OPTIMADE field names and the field names in the database , e.g. ((\"elements\", \"custom_elements_field\"))
.
LENGTH_ALIASES
tuple[tuple[str, str], ...]
a tuple of aliases between a field name and another field that defines its length, to be used when querying, e.g. ((\"elements\", \"nelements\"))
. e.g. ((\"elements\", \"custom_elements_field\"))
.
ENTRY_RESOURCE_CLASS
type[EntryResource]
The entry type that this mapper corresponds to.
PROVIDER_FIELDS
tuple[str, ...]
a tuple of extra field names that this mapper should support when querying with the database prefix.
TOP_LEVEL_NON_ATTRIBUTES_FIELDS
set[str]
the set of top-level field names common to all endpoints.
SUPPORTED_PREFIXES
set[str]
The set of prefixes registered by this mapper.
ALL_ATTRIBUTES
set[str]
The set of attributes defined across the entry resource class and the server configuration.
ENTRY_RESOURCE_ATTRIBUTES
dict[str, Any]
A dictionary of attributes and their definitions defined by the schema of the entry resource class.
ENDPOINT
str
The expected endpoint name for this resource, as defined by the type
in the schema of the entry resource class.
optimade/server/mappers/entries.py
class BaseResourceMapper:\n \"\"\"Generic Resource Mapper that defines and performs the mapping\n between objects in the database and the resource objects defined by\n the specification.\n\n Attributes:\n ALIASES: a tuple of aliases between\n OPTIMADE field names and the field names in the database ,\n e.g. `((\"elements\", \"custom_elements_field\"))`.\n LENGTH_ALIASES: a tuple of aliases between\n a field name and another field that defines its length, to be used\n when querying, e.g. `((\"elements\", \"nelements\"))`.\n e.g. `((\"elements\", \"custom_elements_field\"))`.\n ENTRY_RESOURCE_CLASS: The entry type that this mapper corresponds to.\n PROVIDER_FIELDS: a tuple of extra field names that this\n mapper should support when querying with the database prefix.\n TOP_LEVEL_NON_ATTRIBUTES_FIELDS: the set of top-level\n field names common to all endpoints.\n SUPPORTED_PREFIXES: The set of prefixes registered by this mapper.\n ALL_ATTRIBUTES: The set of attributes defined across the entry\n resource class and the server configuration.\n ENTRY_RESOURCE_ATTRIBUTES: A dictionary of attributes and their definitions\n defined by the schema of the entry resource class.\n ENDPOINT: The expected endpoint name for this resource, as defined by\n the `type` in the schema of the entry resource class.\n\n \"\"\"\n\n try:\n from optimade.server.data import providers as PROVIDERS # type: ignore\n except (ImportError, ModuleNotFoundError):\n PROVIDERS = {}\n\n KNOWN_PROVIDER_PREFIXES: set[str] = {\n prov[\"id\"] for prov in PROVIDERS.get(\"data\", [])\n }\n ALIASES: tuple[tuple[str, str], ...] = ()\n LENGTH_ALIASES: tuple[tuple[str, str], ...] = ()\n PROVIDER_FIELDS: tuple[str, ...] = ()\n ENTRY_RESOURCE_CLASS: type[EntryResource] = EntryResource\n RELATIONSHIP_ENTRY_TYPES: set[str] = {\"references\", \"structures\"}\n TOP_LEVEL_NON_ATTRIBUTES_FIELDS: set[str] = {\"id\", \"type\", \"relationships\", \"links\"}\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n\n @classproperty\n @lru_cache(maxsize=1)\n def SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n\n @classproperty\n def ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n\n @classproperty\n def ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n\n @classproperty\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n\n @classmethod\n @lru_cache(maxsize=128)\n def length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n\n @classmethod\n @lru_cache(maxsize=128)\n def get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n\n @classmethod\n @lru_cache(maxsize=128)\n def alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n\n @classmethod\n @lru_cache(maxsize=128)\n def get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n\n @classmethod\n @lru_cache(maxsize=128)\n def alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n\n @classmethod\n @lru_cache(maxsize=NUM_ENTRY_TYPES)\n def get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n\n @classmethod\n def map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE.\n\n Starting from a MongoDB document `doc`, map the DB fields to the corresponding OPTIMADE fields.\n Then, the fields are all added to the top-level field \"attributes\",\n with the exception of other top-level fields, defined in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS`.\n All fields not in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS` + \"attributes\" will be removed.\n Finally, the `type` is given the value of the specified `cls.ENDPOINT`.\n\n Parameters:\n doc: A resource object in MongoDB format.\n\n Returns:\n A resource object in OPTIMADE format.\n\n \"\"\"\n mapping = ((real, alias) for alias, real in cls.all_aliases())\n newdoc = {}\n reals = {real for _, real in cls.all_aliases()}\n for key in doc:\n if key not in reals:\n newdoc[key] = doc[key]\n for real, alias in mapping:\n if real in doc:\n newdoc[alias] = doc[real]\n\n if \"attributes\" in newdoc:\n raise Exception(\"Will overwrite doc field!\")\n attributes = newdoc.copy()\n\n for field in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n value = attributes.pop(field, None)\n if value is not None:\n newdoc[field] = value\n for field in list(newdoc.keys()):\n if field not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n del newdoc[field]\n\n newdoc[\"type\"] = cls.ENDPOINT\n newdoc[\"attributes\"] = attributes\n\n return newdoc\n\n @classmethod\n def deserialize(\n cls, results: Union[dict, Iterable[dict]]\n ) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ALL_ATTRIBUTES","title":"ALL_ATTRIBUTES()
","text":"Returns all attributes served by this entry.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ENDPOINT","title":"ENDPOINT()
cached
","text":"Returns the expected endpoint for this mapper, corresponding to the type
property of the resource class.
optimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.ENTRY_RESOURCE_ATTRIBUTES","title":"ENTRY_RESOURCE_ATTRIBUTES()
","text":"Returns the dictionary of attributes defined by the underlying entry resource class.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.SUPPORTED_PREFIXES","title":"SUPPORTED_PREFIXES()
cached
","text":"A set of prefixes handled by this entry type.
Note
This implementation only includes the provider prefix, but in the future this property may be extended to include other namespaces (for serving fields from, e.g., other providers or domain-specific terms).
Source code inoptimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=1)\ndef SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.alias_for","title":"alias_for(field)
cached
classmethod
","text":"Return aliased field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_backend_field()
.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type Descriptionstr
Aliased field as found in all_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.alias_of","title":"alias_of(field)
cached
classmethod
","text":"Return de-aliased field name, if it exists, otherwise return the input field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_optimade_field()
.
Parameters:
Name Type Description Defaultfield
str
Field name to be de-aliased.
requiredReturns:
Type Descriptionstr
De-aliased field name, falling back to returning field
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.all_aliases","title":"all_aliases()
cached
classmethod
","text":"Returns all of the associated aliases for this entry type, including those defined by the server config. The first member of each tuple is the OPTIMADE-compliant field name, the second is the backend-specific field name.
Returns:
Type DescriptionIterable[tuple[str, str]]
A tuple of alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases","title":"all_length_aliases()
cached
classmethod
","text":"Returns all of the associated length aliases for this class, including those defined by the server config.
Returns:
Type Descriptiontuple[tuple[str, str], ...]
A tuple of length alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.deserialize","title":"deserialize(results)
classmethod
","text":"Converts the raw database entries for this class into serialized models, mapping the data along the way.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef deserialize(\n cls, results: Union[dict, Iterable[dict]]\n) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_backend_field","title":"get_backend_field(optimade_field)
cached
classmethod
","text":"Return the field name configured for the particular underlying database for the passed OPTIMADE field name, that would be used in an API filter.
Aliases are read from all_aliases()
.
If a dot-separated OPTIMADE field is provided, e.g., species.mass
, only the first part will be mapped. This means for an (OPTIMADE, DB) alias of (species
, kinds
), get_backend_fields(\"species.mass\")
will return kinds.mass
.
Parameters:
Name Type Description Defaultoptimade_field
str
The OPTIMADE field to attempt to map to the backend-specific field.
requiredExamples:
>>> get_backend_field(\"chemical_formula_anonymous\")\n'formula_anon'\n>>> get_backend_field(\"formula_anon\")\n'formula_anon'\n>>> get_backend_field(\"_exmpl_custom_provider_field\")\n'custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in the query to the backend.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field","title":"get_optimade_field(backend_field)
cached
classmethod
","text":"Return the corresponding OPTIMADE field name for the underlying database field, ready to be used to construct the OPTIMADE-compliant JSON response.
Aliases are read from all_aliases()
.
Parameters:
Name Type Description Defaultbackend_field
str
The backend field to attempt to map to an OPTIMADE field.
requiredExamples:
>>> get_optimade_field(\"chemical_formula_anonymous\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"formula_anon\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"custom_provider_field\")\n'_exmpl_custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in an OPTIMADE-compliant response.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.get_required_fields","title":"get_required_fields()
cached
classmethod
","text":"Get REQUIRED response fields.
Returns:
Type Descriptionset
REQUIRED response fields.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.length_alias_for","title":"length_alias_for(field)
cached
classmethod
","text":"Returns the length alias for the particular field, or None
if no such alias is found.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type DescriptionOptional[str]
Aliased field as found in all_length_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.BaseResourceMapper.map_back","title":"map_back(doc)
classmethod
","text":"Map properties from MongoDB to OPTIMADE.
Starting from a MongoDB document doc
, map the DB fields to the corresponding OPTIMADE fields. Then, the fields are all added to the top-level field \"attributes\", with the exception of other top-level fields, defined in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS
. All fields not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS
+ \"attributes\" will be removed. Finally, the type
is given the value of the specified cls.ENDPOINT
.
Parameters:
Name Type Description Defaultdoc
dict
A resource object in MongoDB format.
requiredReturns:
Type Descriptiondict
A resource object in OPTIMADE format.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE.\n\n Starting from a MongoDB document `doc`, map the DB fields to the corresponding OPTIMADE fields.\n Then, the fields are all added to the top-level field \"attributes\",\n with the exception of other top-level fields, defined in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS`.\n All fields not in `cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS` + \"attributes\" will be removed.\n Finally, the `type` is given the value of the specified `cls.ENDPOINT`.\n\n Parameters:\n doc: A resource object in MongoDB format.\n\n Returns:\n A resource object in OPTIMADE format.\n\n \"\"\"\n mapping = ((real, alias) for alias, real in cls.all_aliases())\n newdoc = {}\n reals = {real for _, real in cls.all_aliases()}\n for key in doc:\n if key not in reals:\n newdoc[key] = doc[key]\n for real, alias in mapping:\n if real in doc:\n newdoc[alias] = doc[real]\n\n if \"attributes\" in newdoc:\n raise Exception(\"Will overwrite doc field!\")\n attributes = newdoc.copy()\n\n for field in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n value = attributes.pop(field, None)\n if value is not None:\n newdoc[field] = value\n for field in list(newdoc.keys()):\n if field not in cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS:\n del newdoc[field]\n\n newdoc[\"type\"] = cls.ENDPOINT\n newdoc[\"attributes\"] = attributes\n\n return newdoc\n
"},{"location":"api_reference/server/mappers/entries/#optimade.server.mappers.entries.classproperty","title":"classproperty
","text":" Bases: property
A simple extension of the property decorator that binds to types rather than instances.
Modelled on this StackOverflow answer with some tweaks to allow mkdocstrings to do its thing.
Source code inoptimade/server/mappers/entries.py
class classproperty(property):\n \"\"\"A simple extension of the property decorator that binds to types\n rather than instances.\n\n Modelled on this [StackOverflow answer](https://stackoverflow.com/a/5192374)\n with some tweaks to allow mkdocstrings to do its thing.\n\n \"\"\"\n\n def __init__(self, func):\n self.__name__ = func.__name__\n self.__module__ = func.__module__\n self.__doc__ = func.__doc__\n self.__wrapped__ = func\n\n def __get__(self, _: Any, owner: Optional[type] = None) -> Any:\n return self.__wrapped__(owner)\n
"},{"location":"api_reference/server/mappers/links/","title":"links","text":""},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper","title":"LinksMapper
","text":" Bases: BaseResourceMapper
optimade/server/mappers/links.py
class LinksMapper(BaseResourceMapper):\n ENTRY_RESOURCE_CLASS = LinksResource\n\n @classmethod\n def map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE\n\n :param doc: A resource object in MongoDB format\n :type doc: dict\n\n :return: A resource object in OPTIMADE format\n :rtype: dict\n \"\"\"\n type_ = doc[\"type\"]\n newdoc = super().map_back(doc)\n newdoc[\"type\"] = type_\n return newdoc\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ALL_ATTRIBUTES","title":"ALL_ATTRIBUTES()
","text":"Returns all attributes served by this entry.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ALL_ATTRIBUTES(cls) -> set[str]:\n \"\"\"Returns all attributes served by this entry.\"\"\"\n from optimade.server.config import CONFIG\n\n return (\n set(cls.ENTRY_RESOURCE_ATTRIBUTES)\n .union(\n cls.get_optimade_field(field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, str)\n )\n .union(\n cls.get_optimade_field(field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, ())\n if isinstance(field, dict)\n )\n .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS})\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ENDPOINT","title":"ENDPOINT()
cached
","text":"Returns the expected endpoint for this mapper, corresponding to the type
property of the resource class.
optimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef ENDPOINT(cls) -> str:\n \"\"\"Returns the expected endpoint for this mapper, corresponding\n to the `type` property of the resource class.\n\n \"\"\"\n endpoint = cls.ENTRY_RESOURCE_CLASS.model_fields[\"type\"].default\n if not endpoint and not isinstance(endpoint, str):\n raise ValueError(\"Type not set for this entry type!\")\n return endpoint\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.ENTRY_RESOURCE_ATTRIBUTES","title":"ENTRY_RESOURCE_ATTRIBUTES()
","text":"Returns the dictionary of attributes defined by the underlying entry resource class.
Source code inoptimade/server/mappers/entries.py
@classproperty\ndef ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]:\n \"\"\"Returns the dictionary of attributes defined by the underlying entry resource class.\"\"\"\n from optimade.server.schemas import retrieve_queryable_properties\n\n return retrieve_queryable_properties(cls.ENTRY_RESOURCE_CLASS)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.SUPPORTED_PREFIXES","title":"SUPPORTED_PREFIXES()
cached
","text":"A set of prefixes handled by this entry type.
Note
This implementation only includes the provider prefix, but in the future this property may be extended to include other namespaces (for serving fields from, e.g., other providers or domain-specific terms).
Source code inoptimade/server/mappers/entries.py
@classproperty\n@lru_cache(maxsize=1)\ndef SUPPORTED_PREFIXES(cls) -> set[str]:\n \"\"\"A set of prefixes handled by this entry type.\n\n !!! note\n This implementation only includes the provider prefix,\n but in the future this property may be extended to include other\n namespaces (for serving fields from, e.g., other providers or\n domain-specific terms).\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return {CONFIG.provider.prefix}\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.alias_for","title":"alias_for(field)
cached
classmethod
","text":"Return aliased field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_backend_field()
.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type Descriptionstr
Aliased field as found in all_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_for(cls, field: str) -> str:\n \"\"\"Return aliased field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_backend_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_backend_field].\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n \"\"\"\n warnings.warn(\n \"The `.alias_for(...)` method is deprecated, please use `.get_backend_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_backend_field(field)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.alias_of","title":"alias_of(field)
cached
classmethod
","text":"Return de-aliased field name, if it exists, otherwise return the input field name.
Deprecated
This method is deprecated could be removed without further warning. Please use get_optimade_field()
.
Parameters:
Name Type Description Defaultfield
str
Field name to be de-aliased.
requiredReturns:
Type Descriptionstr
De-aliased field name, falling back to returning field
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef alias_of(cls, field: str) -> str:\n \"\"\"Return de-aliased field name, if it exists,\n otherwise return the input field name.\n\n !!! warning \"Deprecated\"\n This method is deprecated could be removed without further warning. Please use\n [`get_optimade_field()`][optimade.server.mappers.entries.BaseResourceMapper.get_optimade_field].\n\n Parameters:\n field: Field name to be de-aliased.\n\n Returns:\n De-aliased field name, falling back to returning `field`.\n\n \"\"\"\n warnings.warn(\n \"The `.alias_of(...)` method is deprecated, please use `.get_optimade_field(...)`.\",\n DeprecationWarning,\n )\n return cls.get_optimade_field(field)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.all_aliases","title":"all_aliases()
cached
classmethod
","text":"Returns all of the associated aliases for this entry type, including those defined by the server config. The first member of each tuple is the OPTIMADE-compliant field name, the second is the backend-specific field name.
Returns:
Type DescriptionIterable[tuple[str, str]]
A tuple of alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_aliases(cls) -> Iterable[tuple[str, str]]:\n \"\"\"Returns all of the associated aliases for this entry type,\n including those defined by the server config. The first member\n of each tuple is the OPTIMADE-compliant field name, the second\n is the backend-specific field name.\n\n Returns:\n A tuple of alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return (\n tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, str)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field['name']}\", field[\"name\"])\n if not field[\"name\"].startswith(\"_\")\n else (field[\"name\"], field[\"name\"])\n for field in CONFIG.provider_fields.get(cls.ENDPOINT, [])\n if isinstance(field, dict)\n )\n + tuple(\n (f\"_{CONFIG.provider.prefix}_{field}\", field)\n if not field.startswith(\"_\")\n else (field, field)\n for field in cls.PROVIDER_FIELDS\n )\n + tuple(CONFIG.aliases.get(cls.ENDPOINT, {}).items())\n + cls.ALIASES\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.all_length_aliases","title":"all_length_aliases()
cached
classmethod
","text":"Returns all of the associated length aliases for this class, including those defined by the server config.
Returns:
Type Descriptiontuple[tuple[str, str], ...]
A tuple of length alias tuples.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef all_length_aliases(cls) -> tuple[tuple[str, str], ...]:\n \"\"\"Returns all of the associated length aliases for this class,\n including those defined by the server config.\n\n Returns:\n A tuple of length alias tuples.\n\n \"\"\"\n from optimade.server.config import CONFIG\n\n return cls.LENGTH_ALIASES + tuple(\n CONFIG.length_aliases.get(cls.ENDPOINT, {}).items()\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.deserialize","title":"deserialize(results)
classmethod
","text":"Converts the raw database entries for this class into serialized models, mapping the data along the way.
Source code inoptimade/server/mappers/entries.py
@classmethod\ndef deserialize(\n cls, results: Union[dict, Iterable[dict]]\n) -> Union[list[EntryResource], EntryResource]:\n \"\"\"Converts the raw database entries for this class into serialized models,\n mapping the data along the way.\n\n \"\"\"\n if isinstance(results, dict):\n return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results))\n\n return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results]\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_backend_field","title":"get_backend_field(optimade_field)
cached
classmethod
","text":"Return the field name configured for the particular underlying database for the passed OPTIMADE field name, that would be used in an API filter.
Aliases are read from all_aliases()
.
If a dot-separated OPTIMADE field is provided, e.g., species.mass
, only the first part will be mapped. This means for an (OPTIMADE, DB) alias of (species
, kinds
), get_backend_fields(\"species.mass\")
will return kinds.mass
.
Parameters:
Name Type Description Defaultoptimade_field
str
The OPTIMADE field to attempt to map to the backend-specific field.
requiredExamples:
>>> get_backend_field(\"chemical_formula_anonymous\")\n'formula_anon'\n>>> get_backend_field(\"formula_anon\")\n'formula_anon'\n>>> get_backend_field(\"_exmpl_custom_provider_field\")\n'custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in the query to the backend.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_backend_field(cls, optimade_field: str) -> str:\n \"\"\"Return the field name configured for the particular\n underlying database for the passed OPTIMADE field name, that would\n be used in an API filter.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n If a dot-separated OPTIMADE field is provided, e.g., `species.mass`, only the first part will be mapped.\n This means for an (OPTIMADE, DB) alias of (`species`, `kinds`), `get_backend_fields(\"species.mass\")`\n will return `kinds.mass`.\n\n Arguments:\n optimade_field: The OPTIMADE field to attempt to map to the backend-specific field.\n\n Examples:\n >>> get_backend_field(\"chemical_formula_anonymous\")\n 'formula_anon'\n >>> get_backend_field(\"formula_anon\")\n 'formula_anon'\n >>> get_backend_field(\"_exmpl_custom_provider_field\")\n 'custom_provider_field'\n\n Returns:\n The mapped field name to be used in the query to the backend.\n\n \"\"\"\n split = optimade_field.split(\".\")\n alias = dict(cls.all_aliases()).get(split[0], None)\n if alias is not None:\n return alias + (\".\" + \".\".join(split[1:]) if len(split) > 1 else \"\")\n return optimade_field\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_optimade_field","title":"get_optimade_field(backend_field)
cached
classmethod
","text":"Return the corresponding OPTIMADE field name for the underlying database field, ready to be used to construct the OPTIMADE-compliant JSON response.
Aliases are read from all_aliases()
.
Parameters:
Name Type Description Defaultbackend_field
str
The backend field to attempt to map to an OPTIMADE field.
requiredExamples:
>>> get_optimade_field(\"chemical_formula_anonymous\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"formula_anon\")\n'chemical_formula_anonymous'\n>>> get_optimade_field(\"custom_provider_field\")\n'_exmpl_custom_provider_field'\n
Returns:
Type Descriptionstr
The mapped field name to be used in an OPTIMADE-compliant response.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef get_optimade_field(cls, backend_field: str) -> str:\n \"\"\"Return the corresponding OPTIMADE field name for the underlying database field,\n ready to be used to construct the OPTIMADE-compliant JSON response.\n\n Aliases are read from\n [`all_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_aliases].\n\n Arguments:\n backend_field: The backend field to attempt to map to an OPTIMADE field.\n\n Examples:\n >>> get_optimade_field(\"chemical_formula_anonymous\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"formula_anon\")\n 'chemical_formula_anonymous'\n >>> get_optimade_field(\"custom_provider_field\")\n '_exmpl_custom_provider_field'\n\n Returns:\n The mapped field name to be used in an OPTIMADE-compliant response.\n\n \"\"\"\n return {alias: real for real, alias in cls.all_aliases()}.get(\n backend_field, backend_field\n )\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.get_required_fields","title":"get_required_fields()
cached
classmethod
","text":"Get REQUIRED response fields.
Returns:
Type Descriptionset
REQUIRED response fields.
Source code inoptimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=NUM_ENTRY_TYPES)\ndef get_required_fields(cls) -> set:\n \"\"\"Get REQUIRED response fields.\n\n Returns:\n REQUIRED response fields.\n\n \"\"\"\n return cls.TOP_LEVEL_NON_ATTRIBUTES_FIELDS\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.length_alias_for","title":"length_alias_for(field)
cached
classmethod
","text":"Returns the length alias for the particular field, or None
if no such alias is found.
Parameters:
Name Type Description Defaultfield
str
OPTIMADE field name.
requiredReturns:
Type DescriptionOptional[str]
Aliased field as found in all_length_aliases()
.
optimade/server/mappers/entries.py
@classmethod\n@lru_cache(maxsize=128)\ndef length_alias_for(cls, field: str) -> Optional[str]:\n \"\"\"Returns the length alias for the particular field,\n or `None` if no such alias is found.\n\n Parameters:\n field: OPTIMADE field name.\n\n Returns:\n Aliased field as found in [`all_length_aliases()`][optimade.server.mappers.entries.BaseResourceMapper.all_length_aliases].\n\n \"\"\"\n return dict(cls.all_length_aliases()).get(field, None)\n
"},{"location":"api_reference/server/mappers/links/#optimade.server.mappers.links.LinksMapper.map_back","title":"map_back(doc)
classmethod
","text":"Map properties from MongoDB to OPTIMADE
:param doc: A resource object in MongoDB format :type doc: dict
:return: A resource object in OPTIMADE format :rtype: dict
Source code inoptimade/server/mappers/links.py
@classmethod\ndef map_back(cls, doc: dict) -> dict:\n \"\"\"Map properties from MongoDB to OPTIMADE\n\n :param doc: A resource object in MongoDB format\n :type doc: dict\n\n :return: A resource object in OPTIMADE format\n :rtype: dict\n \"\"\"\n type_ = doc[\"type\"]\n newdoc = super().map_back(doc)\n newdoc[\"type\"] = type_\n return newdoc\n
"},{"location":"api_reference/server/mappers/references/","title":"references","text":""},{"location":"api_reference/server/mappers/structures/","title":"structures","text":""},{"location":"api_reference/server/routers/index_info/","title":"index_info","text":""},{"location":"api_reference/server/routers/info/","title":"info","text":""},{"location":"api_reference/server/routers/landing/","title":"landing","text":"OPTIMADE landing page router.
"},{"location":"api_reference/server/routers/landing/#optimade.server.routers.landing.landing","title":"landing(request)
async
","text":"Show a human-readable landing page when the base URL is accessed.
Source code inoptimade/server/routers/landing.py
async def landing(request: Request):\n \"\"\"Show a human-readable landing page when the base URL is accessed.\"\"\"\n return render_landing_page(str(request.url))\n
"},{"location":"api_reference/server/routers/landing/#optimade.server.routers.landing.render_landing_page","title":"render_landing_page(url)
cached
","text":"Render and cache the landing page.
This function uses the template file ./static/landing_page.html
, adapted from the original Jinja template. Instead of Jinja, some basic string replacement is used to fill out the fields from the server configuration.
Careful
The removal of Jinja means that the fields are no longer validated as web safe before inclusion in the template.
Source code inoptimade/server/routers/landing.py
@lru_cache\ndef render_landing_page(url: str) -> HTMLResponse:\n \"\"\"Render and cache the landing page.\n\n This function uses the template file `./static/landing_page.html`, adapted\n from the original Jinja template. Instead of Jinja, some basic string\n replacement is used to fill out the fields from the server configuration.\n\n !!! warning \"Careful\"\n The removal of Jinja means that the fields are no longer validated as\n web safe before inclusion in the template.\n\n \"\"\"\n meta = meta_values(url, 1, 1, more_data_available=False, schema=CONFIG.schema_url)\n major_version = __api_version__.split(\".\")[0]\n versioned_url = f\"{get_base_url(url)}/v{major_version}/\"\n\n if CONFIG.custom_landing_page:\n html = Path(CONFIG.custom_landing_page).resolve().read_text()\n else:\n template_dir = Path(__file__).parent.joinpath(\"static\").resolve()\n html = (template_dir / \"landing_page.html\").read_text()\n\n # Build a dictionary that maps the old Jinja keys to the new simplified replacements\n replacements = {\n \"api_version\": __api_version__,\n }\n\n if meta.provider:\n replacements.update(\n {\n \"provider.name\": meta.provider.name,\n \"provider.prefix\": meta.provider.prefix,\n \"provider.description\": meta.provider.description,\n \"provider.homepage\": str(meta.provider.homepage) or \"\",\n }\n )\n\n if meta.implementation:\n replacements.update(\n {\n \"implementation.name\": meta.implementation.name or \"\",\n \"implementation.version\": meta.implementation.version or \"\",\n \"implementation.source_url\": str(meta.implementation.source_url or \"\"),\n }\n )\n\n for replacement in replacements:\n html = html.replace(f\"{{{{ {replacement} }}}}\", replacements[replacement])\n\n # Build the list of endpoints. The template already opens and closes the `<ul>` tag.\n endpoints_list = [\n f'<li><a href=\"{versioned_url}{endp}\">{versioned_url}{endp}</a></li>'\n for endp in list(ENTRY_COLLECTIONS.keys()) + [\"info\"]\n ]\n html = html.replace(\"{% ENDPOINTS %}\", \"\\n\".join(endpoints_list))\n\n # If the index base URL has been configured, also list it\n index_base_url_html = \"\"\n if CONFIG.index_base_url:\n index_base_url_html = f\"\"\"<h3>Index base URL:</h3>\n<p><a href={CONFIG.index_base_url}>{CONFIG.index_base_url}</a></p>\n\"\"\"\n html = html.replace(\"{% INDEX_BASE_URL %}\", index_base_url_html)\n\n return HTMLResponse(html)\n
"},{"location":"api_reference/server/routers/links/","title":"links","text":""},{"location":"api_reference/server/routers/references/","title":"references","text":""},{"location":"api_reference/server/routers/structures/","title":"structures","text":""},{"location":"api_reference/server/routers/utils/","title":"utils","text":""},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.JSONAPIResponse","title":"JSONAPIResponse
","text":" Bases: JSONResponse
This class simply patches fastapi.responses.JSONResponse
to use the JSON:API 'application/vnd.api+json' MIME type.
optimade/server/routers/utils.py
class JSONAPIResponse(JSONResponse):\n \"\"\"This class simply patches `fastapi.responses.JSONResponse` to use the\n JSON:API 'application/vnd.api+json' MIME type.\n\n \"\"\"\n\n media_type = \"application/vnd.api+json\"\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_base_url","title":"get_base_url(parsed_url_request)
","text":"Get base URL for current server
Take the base URL from the config file, if it exists, otherwise use the request.
Source code inoptimade/server/routers/utils.py
def get_base_url(\n parsed_url_request: Union[\n urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str\n ],\n) -> str:\n \"\"\"Get base URL for current server\n\n Take the base URL from the config file, if it exists, otherwise use the request.\n \"\"\"\n parsed_url_request = (\n urllib.parse.urlparse(parsed_url_request)\n if isinstance(parsed_url_request, str)\n else parsed_url_request\n )\n return (\n CONFIG.base_url.rstrip(\"/\")\n if CONFIG.base_url\n else f\"{parsed_url_request.scheme}://{parsed_url_request.netloc}\"\n )\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_entries","title":"get_entries(collection, request, params)
","text":"Generalized /{entry} endpoint getter
Source code inoptimade/server/routers/utils.py
def get_entries(\n collection: EntryCollection,\n request: Request,\n params: EntryListingQueryParams,\n) -> dict[str, Any]:\n \"\"\"Generalized /{entry} endpoint getter\"\"\"\n from optimade.server.routers import ENTRY_COLLECTIONS\n\n params.check_params(request.query_params)\n (\n results,\n data_returned,\n more_data_available,\n fields,\n include_fields,\n ) = collection.find(params)\n\n include = []\n if getattr(params, \"include\", False):\n include.extend(params.include.split(\",\"))\n\n included = []\n if results is not None:\n included = get_included_relationships(results, ENTRY_COLLECTIONS, include)\n\n if more_data_available:\n # Deduce the `next` link from the current request\n query = urllib.parse.parse_qs(request.url.query)\n query.update(collection.get_next_query_params(params, results))\n\n urlencoded = urllib.parse.urlencode(query, doseq=True)\n base_url = get_base_url(request.url)\n\n links = ToplevelLinks(next=f\"{base_url}{request.url.path}?{urlencoded}\")\n else:\n links = ToplevelLinks(next=None)\n\n if results is not None and (fields or include_fields):\n results = handle_response_fields(results, fields, include_fields) # type: ignore[assignment]\n\n return {\n \"links\": links,\n \"data\": results if results else [],\n \"meta\": meta_values(\n url=request.url,\n data_returned=data_returned,\n data_available=len(collection),\n more_data_available=more_data_available,\n schema=CONFIG.schema_url\n if not CONFIG.is_index\n else CONFIG.index_schema_url,\n ),\n \"included\": included,\n }\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_included_relationships","title":"get_included_relationships(results, ENTRY_COLLECTIONS, include_param)
","text":"Filters the included relationships and makes the appropriate compound request to include them in the response.
Parameters:
Name Type Description Defaultresults
Union[EntryResource, list[EntryResource], dict, list[dict]]
list of returned documents.
requiredENTRY_COLLECTIONS
dict[str, EntryCollection]
dictionary containing collections to query, with key based on endpoint type.
requiredinclude_param
list[str]
list of queried related resources that should be included in included
.
Returns:
Type Descriptionlist[Union[EntryResource, dict[str, Any]]]
Dictionary with the same keys as ENTRY_COLLECTIONS, each containing the list of resource objects for that entry type.
Source code inoptimade/server/routers/utils.py
def get_included_relationships(\n results: Union[EntryResource, list[EntryResource], dict, list[dict]],\n ENTRY_COLLECTIONS: dict[str, EntryCollection],\n include_param: list[str],\n) -> list[Union[EntryResource, dict[str, Any]]]:\n \"\"\"Filters the included relationships and makes the appropriate compound request\n to include them in the response.\n\n Parameters:\n results: list of returned documents.\n ENTRY_COLLECTIONS: dictionary containing collections to query, with key\n based on endpoint type.\n include_param: list of queried related resources that should be included in\n `included`.\n\n Returns:\n Dictionary with the same keys as ENTRY_COLLECTIONS, each containing the list\n of resource objects for that entry type.\n\n \"\"\"\n from collections import defaultdict\n\n if not isinstance(results, list):\n results = [results]\n\n for entry_type in include_param:\n if entry_type not in ENTRY_COLLECTIONS and entry_type != \"\":\n raise BadRequest(\n detail=f\"'{entry_type}' cannot be identified as a valid relationship type. \"\n f\"Known relationship types: {sorted(ENTRY_COLLECTIONS.keys())}\"\n )\n\n endpoint_includes: dict[Any, dict] = defaultdict(dict)\n for doc in results:\n # convert list of references into dict by ID to only included unique IDs\n if doc is None:\n continue\n\n try:\n relationships = doc.relationships # type: ignore\n except AttributeError:\n relationships = doc.get(\"relationships\", None)\n\n if relationships is None:\n continue\n\n if not isinstance(relationships, dict):\n relationships = relationships.model_dump()\n\n for entry_type in ENTRY_COLLECTIONS:\n # Skip entry type if it is not in `include_param`\n if entry_type not in include_param:\n continue\n\n entry_relationship = relationships.get(entry_type, {})\n if entry_relationship is not None:\n refs = entry_relationship.get(\"data\", [])\n for ref in refs:\n if ref[\"id\"] not in endpoint_includes[entry_type]:\n endpoint_includes[entry_type][ref[\"id\"]] = ref\n\n included: dict[\n str,\n Union[list[EntryResource], list[dict[str, Any]]],\n ] = {}\n for entry_type in endpoint_includes:\n compound_filter = \" OR \".join(\n [f'id=\"{ref_id}\"' for ref_id in endpoint_includes[entry_type]]\n )\n params = EntryListingQueryParams(\n filter=compound_filter,\n response_format=\"json\",\n response_fields=\"\",\n sort=\"\",\n page_limit=0,\n page_offset=0,\n )\n\n # still need to handle pagination\n ref_results, _, _, _, _ = ENTRY_COLLECTIONS[entry_type].find(params)\n if ref_results is None:\n ref_results = []\n included[entry_type] = ref_results # type: ignore[assignment]\n\n # flatten dict by endpoint to list\n return [obj for endp in included.values() for obj in endp]\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.get_providers","title":"get_providers(add_mongo_id=False)
","text":"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).
Fallback order if providers.optimade.org is not available:
providers
' list of providers./links
-endpoint.Parameters:
Name Type Description Defaultadd_mongo_id
bool
Whether to populate the _id
field of the provider with MongoDB ObjectID.
False
Returns:
Type Descriptionlist
List of raw JSON-decoded providers including MongoDB object IDs.
Source code inoptimade/utils.py
def get_providers(add_mongo_id: bool = False) -> list:\n \"\"\"Retrieve Materials-Consortia providers (from https://providers.optimade.org/v1/links).\n\n Fallback order if providers.optimade.org is not available:\n\n 1. Try Materials-Consortia/providers on GitHub.\n 2. Try submodule `providers`' list of providers.\n 3. Log warning that providers list from Materials-Consortia is not included in the\n `/links`-endpoint.\n\n Arguments:\n add_mongo_id: Whether to populate the `_id` field of the provider with MongoDB\n ObjectID.\n\n Returns:\n List of raw JSON-decoded providers including MongoDB object IDs.\n\n \"\"\"\n import json\n\n import requests\n\n for provider_list_url in PROVIDER_LIST_URLS:\n try:\n providers = requests.get(provider_list_url, timeout=10).json()\n except (\n requests.exceptions.ConnectionError,\n requests.exceptions.ConnectTimeout,\n json.JSONDecodeError,\n requests.exceptions.SSLError,\n ):\n pass\n else:\n break\n else:\n try:\n from optimade.server.data import providers # type: ignore\n except ImportError:\n from optimade.server.logger import LOGGER\n\n LOGGER.warning(\n \"\"\"Could not retrieve a list of providers!\n\n Tried the following resources:\n\n{}\n The list of providers will not be included in the `/links`-endpoint.\n\"\"\".format(\"\".join([f\" * {_}\\n\" for _ in PROVIDER_LIST_URLS]))\n )\n return []\n\n providers_list = []\n for provider in providers.get(\"data\", []):\n # Remove/skip \"exmpl\"\n if provider[\"id\"] == \"exmpl\":\n continue\n\n provider.update(provider.pop(\"attributes\", {}))\n\n # Add MongoDB ObjectId\n if add_mongo_id:\n provider[\"_id\"] = {\n \"$oid\": mongo_id_for_database(provider[\"id\"], provider[\"type\"])\n }\n\n providers_list.append(provider)\n\n return providers_list\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.handle_response_fields","title":"handle_response_fields(results, exclude_fields, include_fields)
","text":"Handle query parameter response_fields
.
It is assumed that all fields are under attributes
. This is due to all other top-level fields are REQUIRED in the response.
Parameters:
Name Type Description Defaultexclude_fields
set[str]
Fields under attributes
to be excluded from the response.
include_fields
set[str]
Fields under attributes
that were requested that should be set to null if missing in the entry.
Returns:
Type Descriptionlist[dict[str, Any]]
List of resulting resources as dictionaries after pruning according to
list[dict[str, Any]]
the response_fields
OPTIMADE URL query parameter.
optimade/server/routers/utils.py
def handle_response_fields(\n results: Union[list[EntryResource], EntryResource, list[dict], dict],\n exclude_fields: set[str],\n include_fields: set[str],\n) -> list[dict[str, Any]]:\n \"\"\"Handle query parameter `response_fields`.\n\n It is assumed that all fields are under `attributes`.\n This is due to all other top-level fields are REQUIRED in the response.\n\n Parameters:\n exclude_fields: Fields under `attributes` to be excluded from the response.\n include_fields: Fields under `attributes` that were requested that should be\n set to null if missing in the entry.\n\n Returns:\n List of resulting resources as dictionaries after pruning according to\n the `response_fields` OPTIMADE URL query parameter.\n\n \"\"\"\n if not isinstance(results, list):\n results = [results]\n\n new_results = []\n while results:\n new_entry = results.pop(0)\n try:\n new_entry = new_entry.model_dump(exclude_unset=True, by_alias=True) # type: ignore[union-attr]\n except AttributeError:\n pass\n\n # Remove fields excluded by their omission in `response_fields`\n for field in exclude_fields:\n if field in new_entry[\"attributes\"]:\n del new_entry[\"attributes\"][field]\n\n # Include missing fields that were requested in `response_fields`\n for field in include_fields:\n if field not in new_entry[\"attributes\"]:\n new_entry[\"attributes\"][field] = None\n\n new_results.append(new_entry)\n\n return new_results\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.meta_values","title":"meta_values(url, data_returned, data_available, more_data_available, schema=None, **kwargs)
","text":"Helper to initialize the meta values
Source code inoptimade/server/routers/utils.py
def meta_values(\n url: Union[urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str],\n data_returned: Optional[int],\n data_available: int,\n more_data_available: bool,\n schema: Optional[str] = None,\n **kwargs,\n) -> ResponseMeta:\n \"\"\"Helper to initialize the meta values\"\"\"\n from optimade.models import ResponseMetaQuery\n\n if isinstance(url, str):\n url = urllib.parse.urlparse(url)\n\n # To catch all (valid) variations of the version part of the URL, a regex is used\n if re.match(r\"/v[0-9]+(\\.[0-9]+){,2}/.*\", url.path) is not None:\n url_path = re.sub(r\"/v[0-9]+(\\.[0-9]+){,2}/\", \"/\", url.path)\n else:\n url_path = url.path\n\n if schema is None:\n schema = CONFIG.schema_url if not CONFIG.is_index else CONFIG.index_schema_url\n\n return ResponseMeta(\n query=ResponseMetaQuery(representation=f\"{url_path}?{url.query}\"),\n api_version=__api_version__,\n time_stamp=datetime.now(),\n data_returned=data_returned,\n more_data_available=more_data_available,\n provider=CONFIG.provider,\n data_available=data_available,\n implementation=CONFIG.implementation,\n schema=schema,\n **kwargs,\n )\n
"},{"location":"api_reference/server/routers/utils/#optimade.server.routers.utils.mongo_id_for_database","title":"mongo_id_for_database(database_id, database_type)
","text":"Produce a MongoDB ObjectId for a database
Source code inoptimade/utils.py
def mongo_id_for_database(database_id: str, database_type: str) -> str:\n \"\"\"Produce a MongoDB ObjectId for a database\"\"\"\n from bson.objectid import ObjectId\n\n oid = f\"{database_id}{database_type}\"\n if len(oid) > 12:\n oid = oid[:12]\n elif len(oid) < 12:\n oid = f\"{oid}{'0' * (12 - len(oid))}\"\n\n return str(ObjectId(oid.encode(\"UTF-8\")))\n
"},{"location":"api_reference/server/routers/versions/","title":"versions","text":""},{"location":"api_reference/server/routers/versions/#optimade.server.routers.versions.get_versions","title":"get_versions()
","text":"Respond with the text/csv representation for the served versions.
Source code inoptimade/server/routers/versions.py
@router.get(\n \"/versions\",\n tags=[\"Versions\"],\n response_class=CsvResponse,\n)\ndef get_versions() -> CsvResponse:\n \"\"\"Respond with the text/csv representation for the served versions.\"\"\"\n version = BASE_URL_PREFIXES[\"major\"].replace(\"/v\", \"\")\n response = f\"version\\n{version}\"\n return CsvResponse(content=response)\n
"},{"location":"api_reference/validator/config/","title":"config","text":"This submodule defines constant values and definitions from the OPTIMADE specification for use by the validator.
The VALIDATOR_CONFIG
object can be imported and modified before calling the validator inside a Python script to customise the hardcoded values.
ValidatorConfig
","text":" Bases: BaseSettings
This class stores validator config parameters in a way that can be easily modified for testing niche implementations. Many of these fields are determined by the specification directly, but it may be desirable to modify them in certain cases.
Source code inoptimade/validator/config.py
class ValidatorConfig(BaseSettings):\n \"\"\"This class stores validator config parameters in a way that\n can be easily modified for testing niche implementations. Many\n of these fields are determined by the specification directly,\n but it may be desirable to modify them in certain cases.\n\n \"\"\"\n\n response_classes: dict[str, Any] = Field(\n _RESPONSE_CLASSES,\n description=\"Dictionary containing the mapping between endpoints and response classes for the main database\",\n )\n\n response_classes_index: dict[str, Any] = Field(\n _RESPONSE_CLASSES_INDEX,\n description=\"Dictionary containing the mapping between endpoints and response classes for the index meta-database\",\n )\n\n entry_schemas: dict[str, Any] = Field(\n _ENTRY_SCHEMAS, description=\"The entry listing endpoint schemas\"\n )\n\n entry_endpoints: set[str] = Field(\n _ENTRY_ENDPOINTS,\n description=\"The entry endpoints to validate, if present in the API's `/info` response `entry_types_by_format['json']`\",\n )\n\n unique_properties: set[str] = Field(\n _UNIQUE_PROPERTIES,\n description=(\n \"Fields that should be treated as unique indexes for all endpoints, \"\n \"i.e. fields on which filters should return at most one entry.\"\n ),\n )\n\n inclusive_operators: dict[DataType, set[str]] = Field(\n _INCLUSIVE_OPERATORS,\n description=(\n \"Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'inclusive', \"\n \"i.e. those that should return entries with the matching value from the filter.\"\n ),\n )\n\n exclusive_operators: dict[DataType, set[str]] = Field(\n _EXCLUSIVE_OPERATORS,\n description=(\n \"Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'exclusive', \"\n \"i.e. those that should not return entries with the matching value from the filter.\"\n ),\n )\n\n field_specific_overrides: dict[str, dict[SupportLevel, Container[str]]] = Field(\n _FIELD_SPECIFIC_OVERRIDES,\n description=(\n \"Some fields do not require all type comparison operators to be supported. \"\n \"This dictionary allows overriding the list of supported operators for a field, using \"\n \"the field name as a key, and the support level of different operators with a subkey. \"\n \"Queries on fields listed in this way will pass the validator provided the server returns a 501 status.\"\n ),\n )\n\n links_endpoint: str = Field(\"links\", description=\"The name of the links endpoint\")\n versions_endpoint: str = Field(\n \"versions\", description=\"The name of the versions endpoint\"\n )\n\n info_endpoint: str = Field(\"info\", description=\"The name of the info endpoint\")\n non_entry_endpoints: set[str] = Field(\n _NON_ENTRY_ENDPOINTS,\n description=\"The list specification-mandated endpoint names that do not contain entries\",\n )\n top_level_non_attribute_fields: set[str] = Field(\n BaseResourceMapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS,\n description=\"Field names to treat as top-level\",\n )\n\n enum_fallback_values: dict[str, dict[str, list[str]]] = Field(\n _ENUM_DUMMY_VALUES,\n description=\"Provide fallback values for enum fields to use when validating filters.\",\n )\n
"},{"location":"api_reference/validator/utils/","title":"utils","text":"This submodule contains utility methods and models used by the validator. The two main features being:
@test_case
decorator can be used to decorate validation methods and performs error handling, output and logging of test successes and failures.Validator
versions allow for stricter validation of server responses. The standard response classes allow entries to be provided as bare dictionaries, whilst these patched classes force them to be validated with the corresponding entry models themselves.Client
","text":"Source code in optimade/validator/utils.py
class Client: # pragma: no cover\n def __init__(\n self,\n base_url: str,\n max_retries: int = 5,\n headers: Optional[dict[str, str]] = None,\n timeout: Optional[float] = DEFAULT_CONN_TIMEOUT,\n read_timeout: Optional[float] = DEFAULT_READ_TIMEOUT,\n ) -> None:\n \"\"\"Initialises the Client with the given `base_url` without testing\n if it is valid.\n\n Parameters:\n base_url: the base URL of the optimade implementation, including\n request protocol (e.g. `'http://'`) and API version number if necessary.\n\n Examples:\n\n - `'http://example.org/optimade/v1'`,\n - `'www.crystallography.net/cod-test/optimade/v0.10.0/'`\n\n Note: A maximum of one slash (\"/\") is allowed as the last character.\n\n max_retries: The maximum number of attempts to make for each query.\n headers: Dictionary of additional headers to add to every request.\n timeout: Connection timeout in seconds.\n read_timeout: Read timeout in seconds.\n\n \"\"\"\n self.base_url: str = base_url\n self.last_request: Optional[str] = None\n self.response: Optional[requests.Response] = None\n self.max_retries = max_retries\n self.headers = headers or {}\n if \"User-Agent\" not in self.headers:\n self.headers[\"User-Agent\"] = DEFAULT_USER_AGENT_STRING\n self.timeout = timeout or DEFAULT_CONN_TIMEOUT\n self.read_timeout = read_timeout or DEFAULT_READ_TIMEOUT\n\n def get(self, request: str):\n \"\"\"Makes the given request, with a number of retries if being rate limited. The\n request will be prepended with the `base_url` unless the request appears to be an\n absolute URL (i.e. starts with `http://` or `https://`).\n\n Parameters:\n request (str): the request to make against the base URL of this client.\n\n Returns:\n response (requests.models.Response): the response from the server.\n\n Raises:\n SystemExit: if there is no response from the server, or if the URL is invalid.\n ResponseError: if the server does not respond with a non-429 status code within\n the `MAX_RETRIES` attempts.\n\n \"\"\"\n if urllib.parse.urlparse(request, allow_fragments=True).scheme:\n self.last_request = request\n else:\n if request and not request.startswith(\"/\"):\n request = f\"/{request}\"\n self.last_request = f\"{self.base_url}{request}\"\n\n status_code = None\n retries = 0\n errors = []\n while retries < self.max_retries:\n retries += 1\n try:\n self.response = requests.get(\n self.last_request,\n headers=self.headers,\n timeout=(self.timeout, self.read_timeout),\n )\n\n status_code = self.response.status_code\n # If we hit a 429 Too Many Requests status, then try again in 1 second\n if status_code != 429:\n return self.response\n\n # If the connection times out, retry but cache the error\n except requests.exceptions.ConnectionError as exc:\n errors.append(str(exc))\n\n # Read timeouts should prevent further retries\n except requests.exceptions.ReadTimeout as exc:\n raise ResponseError(str(exc)) from exc\n\n except requests.exceptions.MissingSchema:\n sys.exit(\n f\"Unable to make request on {self.last_request}, did you mean http://{self.last_request}?\"\n )\n\n # If the connection failed, or returned a 429, then wait 1 second before retrying\n time.sleep(1)\n\n else:\n message = f\"Hit max retries ({self.max_retries}) on request {self.last_request!r}.\"\n if errors:\n error_str = \"\\n\\t\".join(errors)\n message += f\"\\nErrors:\\n\\t{error_str}\"\n raise ResponseError(message)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.Client.__init__","title":"__init__(base_url, max_retries=5, headers=None, timeout=DEFAULT_CONN_TIMEOUT, read_timeout=DEFAULT_READ_TIMEOUT)
","text":"Initialises the Client with the given base_url
without testing if it is valid.
Parameters:
Name Type Description Defaultbase_url
str
the base URL of the optimade implementation, including request protocol (e.g. 'http://'
) and API version number if necessary.
Examples:
'http://example.org/optimade/v1'
,'www.crystallography.net/cod-test/optimade/v0.10.0/'
Note: A maximum of one slash (\"/\") is allowed as the last character.
requiredmax_retries
int
The maximum number of attempts to make for each query.
5
headers
Optional[dict[str, str]]
Dictionary of additional headers to add to every request.
None
timeout
Optional[float]
Connection timeout in seconds.
DEFAULT_CONN_TIMEOUT
read_timeout
Optional[float]
Read timeout in seconds.
DEFAULT_READ_TIMEOUT
Source code in optimade/validator/utils.py
def __init__(\n self,\n base_url: str,\n max_retries: int = 5,\n headers: Optional[dict[str, str]] = None,\n timeout: Optional[float] = DEFAULT_CONN_TIMEOUT,\n read_timeout: Optional[float] = DEFAULT_READ_TIMEOUT,\n) -> None:\n \"\"\"Initialises the Client with the given `base_url` without testing\n if it is valid.\n\n Parameters:\n base_url: the base URL of the optimade implementation, including\n request protocol (e.g. `'http://'`) and API version number if necessary.\n\n Examples:\n\n - `'http://example.org/optimade/v1'`,\n - `'www.crystallography.net/cod-test/optimade/v0.10.0/'`\n\n Note: A maximum of one slash (\"/\") is allowed as the last character.\n\n max_retries: The maximum number of attempts to make for each query.\n headers: Dictionary of additional headers to add to every request.\n timeout: Connection timeout in seconds.\n read_timeout: Read timeout in seconds.\n\n \"\"\"\n self.base_url: str = base_url\n self.last_request: Optional[str] = None\n self.response: Optional[requests.Response] = None\n self.max_retries = max_retries\n self.headers = headers or {}\n if \"User-Agent\" not in self.headers:\n self.headers[\"User-Agent\"] = DEFAULT_USER_AGENT_STRING\n self.timeout = timeout or DEFAULT_CONN_TIMEOUT\n self.read_timeout = read_timeout or DEFAULT_READ_TIMEOUT\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.Client.get","title":"get(request)
","text":"Makes the given request, with a number of retries if being rate limited. The request will be prepended with the base_url
unless the request appears to be an absolute URL (i.e. starts with http://
or https://
).
Parameters:
Name Type Description Defaultrequest
str
the request to make against the base URL of this client.
requiredReturns:
Name Type Descriptionresponse
Response
the response from the server.
Raises:
Type DescriptionSystemExit
if there is no response from the server, or if the URL is invalid.
ResponseError
if the server does not respond with a non-429 status code within the MAX_RETRIES
attempts.
optimade/validator/utils.py
def get(self, request: str):\n \"\"\"Makes the given request, with a number of retries if being rate limited. The\n request will be prepended with the `base_url` unless the request appears to be an\n absolute URL (i.e. starts with `http://` or `https://`).\n\n Parameters:\n request (str): the request to make against the base URL of this client.\n\n Returns:\n response (requests.models.Response): the response from the server.\n\n Raises:\n SystemExit: if there is no response from the server, or if the URL is invalid.\n ResponseError: if the server does not respond with a non-429 status code within\n the `MAX_RETRIES` attempts.\n\n \"\"\"\n if urllib.parse.urlparse(request, allow_fragments=True).scheme:\n self.last_request = request\n else:\n if request and not request.startswith(\"/\"):\n request = f\"/{request}\"\n self.last_request = f\"{self.base_url}{request}\"\n\n status_code = None\n retries = 0\n errors = []\n while retries < self.max_retries:\n retries += 1\n try:\n self.response = requests.get(\n self.last_request,\n headers=self.headers,\n timeout=(self.timeout, self.read_timeout),\n )\n\n status_code = self.response.status_code\n # If we hit a 429 Too Many Requests status, then try again in 1 second\n if status_code != 429:\n return self.response\n\n # If the connection times out, retry but cache the error\n except requests.exceptions.ConnectionError as exc:\n errors.append(str(exc))\n\n # Read timeouts should prevent further retries\n except requests.exceptions.ReadTimeout as exc:\n raise ResponseError(str(exc)) from exc\n\n except requests.exceptions.MissingSchema:\n sys.exit(\n f\"Unable to make request on {self.last_request}, did you mean http://{self.last_request}?\"\n )\n\n # If the connection failed, or returned a 429, then wait 1 second before retrying\n time.sleep(1)\n\n else:\n message = f\"Hit max retries ({self.max_retries}) on request {self.last_request!r}.\"\n if errors:\n error_str = \"\\n\\t\".join(errors)\n message += f\"\\nErrors:\\n\\t{error_str}\"\n raise ResponseError(message)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.InternalError","title":"InternalError
","text":" Bases: Exception
This exception should be raised when validation throws an unexpected error. These should be counted separately from ResponseError
's and ValidationError
's.
optimade/validator/utils.py
class InternalError(Exception):\n \"\"\"This exception should be raised when validation throws an unexpected error.\n These should be counted separately from `ResponseError`'s and `ValidationError`'s.\n\n \"\"\"\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.ResponseError","title":"ResponseError
","text":" Bases: Exception
This exception should be raised for a manual hardcoded test failure.
Source code inoptimade/validator/utils.py
class ResponseError(Exception):\n \"\"\"This exception should be raised for a manual hardcoded test failure.\"\"\"\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_failure","title":"print_failure(string, **kwargs)
","text":"Print but sad.
Source code inoptimade/validator/utils.py
def print_failure(string: str, **kwargs) -> None:\n \"\"\"Print but sad.\"\"\"\n print(f\"\\033[91m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_notify","title":"print_notify(string, **kwargs)
","text":"Print but louder.
Source code inoptimade/validator/utils.py
def print_notify(string: str, **kwargs) -> None:\n \"\"\"Print but louder.\"\"\"\n print(f\"\\033[94m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_success","title":"print_success(string, **kwargs)
","text":"Print but happy.
Source code inoptimade/validator/utils.py
def print_success(string: str, **kwargs) -> None:\n \"\"\"Print but happy.\"\"\"\n print(f\"\\033[92m\\033[1m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.print_warning","title":"print_warning(string, **kwargs)
","text":"Print but angry.
Source code inoptimade/validator/utils.py
def print_warning(string: str, **kwargs) -> None:\n \"\"\"Print but angry.\"\"\"\n print(f\"\\033[93m{string}\\033[0m\", **kwargs)\n
"},{"location":"api_reference/validator/utils/#optimade.validator.utils.test_case","title":"test_case(test_fn)
","text":"Wrapper for test case functions, which pretty-prints any errors depending on verbosity level, collates the number and severity of test failures, returns the response and summary string to the caller. Any additional positional or keyword arguments are passed directly to test_fn
. The wrapper will intercept the named arguments optional
, multistage
and request
and interpret them according to the docstring for wrapper(...)
below.
Parameters:
Name Type Description Defaulttest_fn
Callable[..., tuple[Any, str]]
Any function that returns an object and a message to print upon success. The function should raise a ResponseError
, ValidationError
or a ManualValidationError
if the test case has failed. The function can return None
to indicate that the test was not appropriate and should be ignored.
optimade/validator/utils.py
def test_case(test_fn: Callable[..., tuple[Any, str]]):\n \"\"\"Wrapper for test case functions, which pretty-prints any errors\n depending on verbosity level, collates the number and severity of\n test failures, returns the response and summary string to the caller.\n Any additional positional or keyword arguments are passed directly\n to `test_fn`. The wrapper will intercept the named arguments\n `optional`, `multistage` and `request` and interpret them according\n to the docstring for `wrapper(...)` below.\n\n Parameters:\n test_fn: Any function that returns an object and a message to\n print upon success. The function should raise a `ResponseError`,\n `ValidationError` or a `ManualValidationError` if the test\n case has failed. The function can return `None` to indicate\n that the test was not appropriate and should be ignored.\n\n \"\"\"\n from functools import wraps\n\n @wraps(test_fn)\n def wrapper(\n validator,\n *args,\n request: Optional[str] = None,\n optional: bool = False,\n multistage: bool = False,\n **kwargs,\n ):\n \"\"\"Wraps a function or validator method and handles\n success, failure and output depending on the keyword\n arguments passed.\n\n Arguments:\n validator: The validator object to accumulate errors/counters.\n *args: Positional arguments passed to the test function.\n request: Description of the request made by the wrapped\n function (e.g. a URL or a summary).\n optional: Whether or not to treat the test as optional.\n multistage: If `True`, no output will be printed for this test,\n and it will not increment the success counter. Errors will be\n handled in the normal way. This can be used to avoid flooding\n the output for mutli-stage tests.\n **kwargs: Extra named arguments passed to the test function.\n\n \"\"\"\n try:\n try:\n if optional and not validator.run_optional_tests:\n result = None\n msg = \"skipping optional\"\n else:\n result, msg = test_fn(validator, *args, **kwargs)\n\n except (json.JSONDecodeError, ResponseError, ValidationError) as exc:\n msg = f\"{exc.__class__.__name__}: {exc}\"\n raise exc\n except Exception as exc:\n msg = f\"{exc.__class__.__name__}: {exc}\"\n raise InternalError(msg)\n\n # Catch SystemExit and KeyboardInterrupt explicitly so that we can pass\n # them to the finally block, where they are immediately raised\n except (Exception, SystemExit, KeyboardInterrupt) as exc:\n result = exc\n traceback = tb.format_exc()\n\n finally:\n # This catches the case of the Client throwing a SystemExit if the server\n # did not respond, the case of the validator \"fail-fast\"'ing and throwing\n # a SystemExit below, and the case of the user interrupting the process manually\n if isinstance(result, (SystemExit, KeyboardInterrupt)):\n raise result\n\n display_request = None\n try:\n display_request = validator.client.last_request\n except AttributeError:\n pass\n if display_request is None:\n display_request = validator.base_url\n if request is not None:\n display_request += \"/\" + request\n\n request = display_request\n\n # If the result was None, return it here and ignore statuses\n if result is None:\n return result, msg\n display_request = requests.utils.requote_uri(request.replace(\"\\n\", \"\")) # type: ignore[union-attr]\n\n if not isinstance(result, Exception):\n if not multistage:\n success_type = \"optional\" if optional else None\n validator.results.add_success(\n f\"{display_request} - {msg}\", success_type\n )\n else:\n message = msg.split(\"\\n\")\n if validator.verbosity > 1:\n # ValidationErrors from pydantic already include very detailed errors\n # that get duplicated in the traceback\n if not isinstance(result, ValidationError):\n message += traceback.split(\"\\n\")\n\n failure_type: Optional[str] = None\n if isinstance(result, InternalError):\n summary = f\"{display_request} - {test_fn.__name__} - failed with internal error\"\n failure_type = \"internal\"\n else:\n summary = (\n f\"{display_request} - {test_fn.__name__} - failed with error\"\n )\n failure_type = \"optional\" if optional else None\n\n validator.results.add_failure(\n summary, \"\\n\".join(message), failure_type=failure_type\n )\n\n # set failure result to None as this is expected by other functions\n result = None\n\n if validator.fail_fast and not optional:\n validator.print_summary()\n raise SystemExit\n\n # Reset the client request so that it can be properly\n # displayed if the next request fails\n if not multistage:\n validator.client.last_request = None\n\n return result, msg\n\n return wrapper\n
"},{"location":"api_reference/validator/validator/","title":"validator","text":"This module contains the ImplementationValidator
class that can be pointed at an OPTIMADE implementation and validated against the specification via the pydantic models implemented in this package.
ImplementationValidator
","text":"Class used to make a series of checks against a particular OPTIMADE implementation over HTTP.
Uses the pydantic models in optimade.models
to validate the response from the server and crawl through the available endpoints.
Attributes:
Name Type Descriptionvalid
Optional[bool]
whether or not the implementation was deemed valid, with None
signifying that tests did not run.
Only works for current version of the specification as defined by optimade.models
.
optimade/validator/validator.py
class ImplementationValidator:\n \"\"\"Class used to make a series of checks against a particular\n OPTIMADE implementation over HTTP.\n\n Uses the pydantic models in [`optimade.models`][optimade.models] to\n validate the response from the server and crawl through the\n available endpoints.\n\n Attributes:\n valid: whether or not the implementation was deemed valid, with\n `None` signifying that tests did not run.\n\n Caution:\n Only works for current version of the specification as defined\n by [`optimade.models`][optimade.models].\n\n \"\"\"\n\n valid: Optional[bool]\n\n def __init__(\n self,\n client: Optional[Any] = None,\n base_url: Optional[str] = None,\n verbosity: int = 0,\n respond_json: bool = False,\n page_limit: int = 4,\n max_retries: int = 5,\n run_optional_tests: bool = True,\n fail_fast: bool = False,\n as_type: Optional[str] = None,\n index: bool = False,\n minimal: bool = False,\n http_headers: Optional[dict[str, str]] = None,\n timeout: float = DEFAULT_CONN_TIMEOUT,\n read_timeout: float = DEFAULT_READ_TIMEOUT,\n ):\n \"\"\"Set up the tests to run, based on constants in this module\n for required endpoints.\n\n Arguments:\n client: A client that has a `.get()` method to obtain the\n response from the implementation. If `None`, then\n [`Client`][optimade.validator.utils.Client] will be used.\n base_url: The URL of the implementation to validate. Unless\n performing \"as_type\" validation, this should point to the\n base of the OPTIMADE implementation.\n verbosity: The verbosity of the output and logging as an integer\n (`0`: critical, `1`: warning, `2`: info, `3`: debug).\n respond_json: If `True`, print only a JSON representation of the\n results of validation to stdout.\n page_limit: The default page limit to apply to filters.\n max_retries: Argument is passed to the client for how many\n attempts to make for a request before failing.\n run_optional_tests: Whether to run the tests on optional\n OPTIMADE features.\n fail_fast: Whether to exit validation after the first failure\n of a mandatory test.\n as_type: An OPTIMADE entry or endpoint type to coerce the response\n from implementation into, e.g. \"structures\". Requires `base_url`\n to be pointed to the corresponding endpoint.\n index: Whether to validate the implementation as an index meta-database.\n minimal: Whether or not to run only a minimal test set.\n http_headers: Dictionary of additional headers to add to every request.\n timeout: The connection timeout to use for all requests (in seconds).\n read_timeout: The read timeout to use for all requests (in seconds).\n\n \"\"\"\n self.verbosity = verbosity\n self.max_retries = max_retries\n self.page_limit = page_limit\n self.index = index\n self.run_optional_tests = run_optional_tests\n self.fail_fast = fail_fast\n self.respond_json = respond_json\n self.minimal = minimal\n\n if as_type is None:\n self.as_type_cls = None\n elif self.index:\n if as_type not in CONF.response_classes_index:\n raise RuntimeError(\n f\"Provided as_type='{as_type}' not allowed for an Index meta-database.\"\n )\n self.as_type_cls = CONF.response_classes_index[as_type]\n elif as_type in (\"structure\", \"reference\"):\n self.as_type_cls = CONF.response_classes[f\"{as_type}s/\"]\n else:\n self.as_type_cls = CONF.response_classes[as_type]\n\n if client is None and base_url is None:\n raise RuntimeError(\n \"Need at least a URL or a client to initialize validator.\"\n )\n if base_url and client:\n raise RuntimeError(\"Please specify at most one of base_url or client.\")\n if client:\n self.client = client\n self.base_url = self.client.base_url\n # If a custom client has been provided, try to set custom headers if they have been specified,\n # but do not overwrite any existing attributes\n if http_headers:\n if not hasattr(self.client, \"headers\"):\n self.client.headers = http_headers\n else:\n print_warning(\n f\"Not using specified request headers {http_headers} with custom client {self.client}.\"\n )\n else:\n while base_url.endswith(\"/\"): # type: ignore[union-attr]\n base_url = base_url[:-1] # type: ignore[index]\n self.base_url = base_url\n self.client = Client(\n self.base_url, # type: ignore[arg-type]\n max_retries=self.max_retries,\n headers=http_headers,\n timeout=timeout,\n read_timeout=read_timeout,\n )\n\n self._setup_log()\n\n self._response_classes = (\n CONF.response_classes_index if self.index else CONF.response_classes\n )\n\n # some simple checks on base_url\n self.base_url = str(self.base_url)\n self.base_url_parsed = urllib.parse.urlparse(self.base_url)\n # only allow filters/endpoints if we are working in \"as_type\" mode\n if self.as_type_cls is None and self.base_url_parsed.query:\n raise SystemExit(\n f\"Base URL {self.base_url} not appropriate: should not contain a filter.\"\n )\n\n self.valid = None\n\n self._test_id_by_type: dict[str, Any] = {}\n self._entry_info_by_type: dict[str, Any] = {}\n\n self.results = ValidatorResults(verbosity=self.verbosity)\n\n def _setup_log(self):\n \"\"\"Define stdout log based on given verbosity.\"\"\"\n self._log = logging.getLogger(\"optimade\").getChild(\"validator\")\n self._log.handlers = []\n stdout_handler = logging.StreamHandler(sys.stdout)\n stdout_handler.setFormatter(\n logging.Formatter(\"%(asctime)s - %(name)s | %(levelname)8s: %(message)s\")\n )\n\n if not self.respond_json:\n self._log.addHandler(stdout_handler)\n else:\n self.verbosity = -1\n\n if self.verbosity == 0:\n self._log.setLevel(logging.CRITICAL)\n elif self.verbosity == 1:\n self._log.setLevel(logging.WARNING)\n elif self.verbosity == 2:\n self._log.setLevel(logging.INFO)\n elif self.verbosity > 0:\n self._log.setLevel(logging.DEBUG)\n\n def print_summary(self):\n \"\"\"Print a summary of the results of validation.\"\"\"\n if self.respond_json:\n print(json.dumps(dataclasses.asdict(self.results), indent=2))\n return\n\n if self.results.failure_messages:\n print(\"\\n\\nFAILURES\")\n print(\"========\\n\")\n for message in self.results.failure_messages:\n print_failure(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.optional_failure_messages:\n print(\"\\n\\nOPTIONAL TEST FAILURES\")\n print(\"======================\\n\")\n for message in self.results.optional_failure_messages:\n print_notify(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.internal_failure_messages:\n print(\"\\n\\nINTERNAL FAILURES\")\n print(\"=================\\n\")\n print(\n \"There were internal validator failures associated with this run.\\n\"\n \"If this problem persists, please report it at:\\n\"\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues/new\\n\"\n )\n\n for message in self.results.internal_failure_messages:\n print_warning(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.valid or (not self.valid and not self.fail_fast):\n final_message = f\"\\n\\nPassed {self.results.success_count} out of {self.results.success_count + self.results.failure_count + self.results.internal_failure_count} tests.\"\n if not self.valid:\n print_failure(final_message)\n else:\n print_success(final_message)\n\n if self.run_optional_tests and not self.fail_fast:\n print(\n f\"Additionally passed {self.results.optional_success_count} out of \"\n f\"{self.results.optional_success_count + self.results.optional_failure_count} optional tests.\"\n )\n\n def validate_implementation(self):\n \"\"\"Run all the test cases on the implementation, or the single type test,\n depending on what options were provided on initialiation.\n\n Sets the `self.valid` attribute to `True` or `False` depending on the\n outcome of the tests.\n\n Raises:\n RuntimeError: If it was not possible to start the validation process.\n\n \"\"\"\n # If a single \"as type\" has been set, only run that test\n if self.as_type_cls is not None:\n self._log.debug(\n \"Validating response of %s with model %s\",\n self.base_url,\n self.as_type_cls,\n )\n self._test_as_type()\n self.valid = not bool(self.results.failure_count)\n self.print_summary()\n return\n\n # Test entire implementation\n if self.verbosity >= 0:\n print(f\"Testing entire implementation at {self.base_url}\")\n info_endp = CONF.info_endpoint\n self._log.debug(\"Testing base info endpoint of %s\", info_endp)\n\n # Get and validate base info to find endpoints\n # If this is not possible, then exit at this stage\n base_info = self._test_info_or_links_endpoint(info_endp)\n if not base_info:\n self._log.critical(\n f\"Unable to deserialize response from introspective {info_endp!r} endpoint. \"\n \"This is required for all further validation, so the validator will now exit.\"\n )\n # Set valid to False to ensure error code 1 is raised at CLI\n self.valid = False\n self.print_summary()\n return\n\n # Grab the provider prefix from base info and use it when looking for provider fields\n self.provider_prefix = None\n meta = base_info.get(\"meta\", {})\n if meta.get(\"provider\") is not None:\n self.provider_prefix = meta[\"provider\"].get(\"prefix\")\n\n # Set the response class for all `/info/entry` endpoints based on `/info` response\n self.available_json_endpoints, _ = self._get_available_endpoints(\n base_info, request=info_endp\n )\n for endp in self.available_json_endpoints:\n self._response_classes[f\"{info_endp}/{endp}\"] = EntryInfoResponse\n\n # Run some tests on the versions endpoint\n self._log.debug(\"Testing versions endpoint %s\", CONF.versions_endpoint)\n self._test_versions_endpoint()\n self._test_bad_version_returns_553()\n\n # Test that entry info endpoints deserialize correctly\n # If they do not, the corresponding entry in _entry_info_by_type\n # is set to False, which must be checked for further validation\n for endp in self.available_json_endpoints:\n entry_info_endpoint = f\"{info_endp}/{endp}\"\n self._log.debug(\"Testing expected info endpoint %s\", entry_info_endpoint)\n self._entry_info_by_type[endp] = self._test_info_or_links_endpoint(\n entry_info_endpoint\n )\n\n # Test that the results from multi-entry-endpoints obey, e.g. page limits,\n # and that all entries can be deserialized with the patched models.\n # These methods also set the test_ids for each type of entry, which are validated\n # in the next loop.\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing multiple entry endpoint of %s\", endp)\n self._test_multi_entry_endpoint(endp)\n\n # Test that the single IDs scraped earlier work with the single entry endpoint\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing single entry request of type %s\", endp)\n self._test_single_entry_endpoint(endp)\n\n # Use the _entry_info_by_type to construct filters on the relevant endpoints\n if not self.minimal:\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing queries on JSON entry endpoint of %s\", endp)\n self._recurse_through_endpoint(endp)\n\n # Test that the links endpoint can be serialized correctly\n self._log.debug(\"Testing %s endpoint\", CONF.links_endpoint)\n self._test_info_or_links_endpoint(CONF.links_endpoint)\n\n self.valid = not (\n self.results.failure_count or self.results.internal_failure_count\n )\n\n self.print_summary()\n\n @test_case\n def _recurse_through_endpoint(self, endp: str) -> tuple[Optional[bool], str]:\n \"\"\"For a given endpoint (`endp`), get the entry type\n and supported fields, testing that all mandatory fields\n are supported, then test queries on every property according\n to the reported type, with optionality decided by the\n specification-level support level for that field.\n\n Parameters:\n endp: Endpoint to be tested.\n\n Returns:\n `True` if endpoint passed the tests, and a string summary.\n\n \"\"\"\n entry_info = self._entry_info_by_type.get(endp)\n\n if not entry_info:\n raise ResponseError(\n f\"Unable to generate filters for endpoint {endp}: 'info/{endp}' response was malformed.\"\n )\n\n _impl_properties = self._check_entry_info(entry_info, endp)\n prop_list = list(_impl_properties.keys())\n\n self._check_response_fields(endp, prop_list)\n\n chosen_entry, _ = self._get_archetypal_entry(endp, prop_list)\n\n if not chosen_entry:\n return (\n None,\n f\"Unable to generate filters for endpoint {endp}: no valid entries found.\",\n )\n\n for prop in _impl_properties:\n # check support level of property\n prop_type = _impl_properties[prop][\"type\"]\n sortable = _impl_properties[prop][\"sortable\"]\n optional = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n == SupportLevel.OPTIONAL\n )\n\n if optional and not self.run_optional_tests:\n continue\n\n self._construct_queries_for_property(\n prop,\n prop_type,\n sortable,\n endp,\n chosen_entry,\n request=f\"; testing queries for {endp}->{prop}\",\n optional=optional,\n )\n\n self._test_unknown_provider_property(endp)\n self._test_completely_unknown_property(endp)\n\n return True, f\"successfully recursed through endpoint {endp}.\"\n\n @test_case\n def _test_completely_unknown_property(self, endp):\n request = f\"{endp}?filter=crazyfield = 2\"\n response, _ = self._get_endpoint(\n request,\n expected_status_code=400,\n )\n\n return True, \"unknown field returned 400 Bad Request, as expected\"\n\n @test_case\n def _test_unknown_provider_property(self, endp):\n dummy_provider_field = \"_crazyprovider_field\"\n\n request = f\"{endp}?filter={dummy_provider_field}=2\"\n response, _ = self._get_endpoint(\n request,\n multistage=True,\n request=request,\n )\n\n if response is not None:\n deserialized, _ = self._deserialize_response(\n response, CONF.response_classes[endp], request=request, multistage=True\n )\n\n return (\n True,\n \"Unknown provider field was ignored when filtering, as expected\",\n )\n\n raise ResponseError(\n \"Failed to handle field from unknown provider; should return without affecting filter results\"\n )\n\n def _check_entry_info(\n self, entry_info: dict[str, Any], endp: str\n ) -> dict[str, dict[str, Any]]:\n \"\"\"Checks that `entry_info` contains all the required properties,\n and returns the property list for the endpoint.\n\n Parameters:\n entry_info: JSON representation of the response from the\n entry info endpoint.\n endp: The name of the entry endpoint.\n\n Returns:\n The list of property names supported by this implementation.\n\n \"\"\"\n properties = entry_info.get(\"data\", {}).get(\"properties\", [])\n self._test_must_properties(\n properties, endp, request=f\"{CONF.info_endpoint}/{endp}\"\n )\n\n return properties\n\n @test_case\n def _test_must_properties(\n self, properties: list[str], endp: str\n ) -> tuple[bool, str]:\n \"\"\"Check that the entry info lists all properties with the \"MUST\"\n support level for this endpoint.\n\n Parameters:\n properties: The list of property names supported by the endpoint.\n endp: The endpoint.\n\n Returns:\n `True` if the properties were found, and a string summary.\n\n \"\"\"\n must_props = {\n prop\n for prop in CONF.entry_schemas.get(endp, {})\n if CONF.entry_schemas[endp].get(prop, {}).get(\"support\")\n == SupportLevel.MUST\n }\n must_props_supported = {prop for prop in properties if prop in must_props}\n missing = must_props - must_props_supported\n if len(missing) != 0:\n raise ResponseError(\n f\"Some 'MUST' properties were missing from info/{endp}: {missing}\"\n )\n\n return True, f\"Found all required properties in entry info for endpoint {endp}\"\n\n @test_case\n def _get_archetypal_entry(\n self, endp: str, properties: list[str]\n ) -> tuple[Optional[dict[str, Any]], str]:\n \"\"\"Get a random entry from the first page of results for this\n endpoint.\n\n Parameters:\n endp: The endpoint to query.\n\n Returns:\n The JSON representation of the chosen entry and the summary message.\n\n\n \"\"\"\n response, message = self._get_endpoint(endp, multistage=True)\n\n if response:\n data = response.json().get(\"data\", [])\n data_returned = len(data)\n if data_returned < 1:\n return (\n None,\n \"Endpoint {endp!r} returned no entries, cannot get archetypal entry or test filtering.\",\n )\n\n archetypal_entry = response.json()[\"data\"][\n random.randint(0, data_returned - 1)\n ]\n if \"id\" not in archetypal_entry:\n raise ResponseError(\n f\"Chosen archetypal entry did not have an ID, cannot proceed: {archetypal_entry!r}\"\n )\n\n return (\n archetypal_entry,\n f\"set archetypal entry for {endp} with ID {archetypal_entry['id']}.\",\n )\n\n raise ResponseError(f\"Failed to get archetypal entry. Details: {message}\")\n\n @test_case\n def _check_response_fields(\n self, endp: str, fields: list[str]\n ) -> tuple[Optional[bool], str]:\n \"\"\"Check that the response field query parameter is obeyed.\n\n Parameters:\n endp: The endpoint to query.\n fields: The known fields for this endpoint to test.\n\n Returns:\n Bool indicating success and a summary message.\n\n \"\"\"\n\n subset_fields = random.sample(fields, min(len(fields) - 1, 3))\n test_query = f\"{endp}?response_fields={','.join(subset_fields)}&page_limit=1\"\n response, _ = self._get_endpoint(test_query, multistage=True)\n\n if response and len(response.json()[\"data\"]) > 0:\n doc = response.json()[\"data\"][0]\n expected_fields = set(subset_fields)\n expected_fields -= CONF.top_level_non_attribute_fields\n\n if \"attributes\" not in doc:\n raise ResponseError(\n f\"Entries are missing `attributes` key.\\nReceived: {doc}\"\n )\n\n returned_fields = set(sorted(list(doc.get(\"attributes\", {}).keys())))\n returned_fields -= CONF.top_level_non_attribute_fields\n\n if expected_fields != returned_fields:\n raise ResponseError(\n f\"Response fields not obeyed by {endp!r}:\\nExpected: {expected_fields}\\nReturned: {returned_fields}\"\n )\n\n return True, \"Successfully limited response fields\"\n\n return (\n None,\n f\"Unable to test adherence to response fields as no entries were returned for endpoint {endp!r}.\",\n )\n\n @test_case\n def _construct_queries_for_property(\n self,\n prop: str,\n prop_type: DataType,\n sortable: bool,\n endp: str,\n chosen_entry: dict[str, Any],\n ) -> tuple[Optional[bool], str]:\n \"\"\"For the given property, property type and chose entry, this method\n runs a series of queries for each field in the entry, testing that the\n initial document is returned where expected.\n\n\n Parameters:\n prop: The property name.\n prop_type: The property type.\n sortable: Whether the implementation has indicated that the field is sortable.\n endp: The corresponding entry endpoint.\n chosen_entry: A JSON respresentation of the chosen entry that will be used to\n construct the filters.\n\n Returns:\n Boolean indicating success (`True`) or failure/irrelevance\n (`None`) and the string summary of the test case.\n\n \"\"\"\n # Explicitly handle top level keys that do not have types in info\n if not chosen_entry:\n raise ResponseError(\n f\"Chosen entry of endpoint '/{endp}' failed validation.\"\n )\n\n if prop == \"type\":\n if chosen_entry.get(\"type\") == endp:\n return True, f\"Successfully validated {prop}\"\n raise ResponseError(\n f\"Chosen entry of endpoint '{endp}' had unexpected or missing type: {chosen_entry.get('type')!r}.\"\n )\n\n prop_type = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"type\")\n if prop_type is None\n else prop_type\n )\n\n if prop_type is None:\n raise ResponseError(\n f\"Cannot validate queries on {prop!r} as field type was not reported in `/info/{endp}`\"\n )\n\n # this is the case of a provider field\n if prop not in CONF.entry_schemas[endp]:\n if self.provider_prefix is None:\n raise ResponseError(\n f\"Found unknown field {prop!r} in `/info/{endp}` and no provider prefix was provided in `/info`\"\n )\n elif not prop.startswith(f\"_{self.provider_prefix}_\"):\n raise ResponseError(\n f\"Found unknown field {prop!r} that did not start with provider prefix '_{self.provider_prefix}_'\"\n )\n return (\n None,\n f\"Found provider field {prop!r}, will not test queries as they are strictly optional.\",\n )\n\n query_optional = (\n CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n == SupportLevel.OPTIONAL\n )\n\n return self._construct_single_property_filters(\n prop, prop_type, sortable, endp, chosen_entry, query_optional\n )\n\n @staticmethod\n def _format_test_value(test_value: Any, prop_type: DataType, operator: str) -> str:\n \"\"\"Formats the test value as a string according to the type of the property.\n\n Parameters:\n test_value: The value to format.\n prop_type: The OPTIMADE data type of the field.\n operator: The operator that will be applied to it.\n\n Returns:\n The value formatted as a string to use in an OPTIMADE filter.\n\n \"\"\"\n if prop_type == DataType.LIST:\n if operator in (\"HAS ALL\", \"HAS ANY\"):\n _vals = sorted(set(test_value))\n if isinstance(test_value[0], str):\n _vals = [f'\"{val}\"' for val in _vals]\n else:\n _vals = [f\"{val}\" for val in _vals]\n _test_value = \",\".join(_vals)\n elif operator == \"LENGTH\":\n _test_value = f\"{len(test_value)}\"\n else:\n if isinstance(test_value[0], str):\n _test_value = f'\"{test_value[0]}\"'\n else:\n _test_value = test_value[0]\n\n elif prop_type in (DataType.STRING, DataType.TIMESTAMP):\n _test_value = f'\"{test_value}\"'\n\n else:\n _test_value = test_value\n\n return _test_value\n\n def _construct_single_property_filters(\n self,\n prop: str,\n prop_type: DataType,\n sortable: bool,\n endp: str,\n chosen_entry: dict[str, Any],\n query_optional: bool,\n ) -> tuple[Optional[bool], str]:\n \"\"\"This method constructs appropriate queries using all operators\n for a certain field and applies some tests:\n\n - inclusive operators return compatible entries, e.g. `>=` always returns\n at least the results of `=`.\n - exclusive operators never return contradictory entries, e.g.\n `nsites=1` never returns the same entries as `nsites!=1`, modulo\n pagination.\n\n Parameters:\n prop: The property name.\n prop_type: The property type.\n sortable: Whether the implementation has indicated that the field is sortable.\n endp: The corresponding entry endpoint.\n chosen_entry: A JSON respresentation of the chosen entry that will be used to\n construct the filters.\n query_optional: Whether to treat query success as optional.\n\n Returns:\n Boolean indicating success (`True`) or failure/irrelevance\n (`None`) and the string summary of the test case.\n\n \"\"\"\n if prop == \"id\":\n test_value = chosen_entry.get(\"id\")\n else:\n test_value = chosen_entry.get(\"attributes\", {}).get(prop, \"_missing\")\n\n if test_value in (\"_missing\", None):\n support = CONF.entry_schemas[endp].get(prop, {}).get(\"support\")\n queryable = CONF.entry_schemas[endp].get(prop, {}).get(\"queryable\")\n submsg = \"had no value\" if test_value == \"_missing\" else \"had `None` value\"\n msg = (\n f\"Chosen entry {submsg} for {prop!r} with support level {support} and queryability {queryable}, \"\n f\"so cannot construct test queries. This field should potentially be removed from the `/info/{endp}` endpoint response.\"\n )\n # None values are allowed for OPTIONAL and SHOULD, so we can just skip\n if support in (\n SupportLevel.OPTIONAL,\n SupportLevel.SHOULD,\n ):\n self._log.info(msg)\n return None, msg\n\n # Otherwise, None values are not allowed for MUST's, and entire missing fields are not allowed\n raise ResponseError(msg)\n\n using_fallback = False\n if prop_type == DataType.LIST:\n if not test_value:\n test_value = CONF.enum_fallback_values.get(endp, {}).get(prop)\n using_fallback = True\n\n if not test_value:\n msg = f\"Not testing filters on field {prop} of type {prop_type} as no test value was found to use in filter.\"\n self._log.warning(msg)\n return None, msg\n if isinstance(test_value[0], dict) or isinstance(test_value[0], list):\n msg = f\"Not testing filters on field {prop} of type {prop_type} with nested dictionary/list test value.\"\n self._log.warning(msg)\n return None, msg\n\n # Try to infer if the test value is a float from its string representation\n # and decide whether to do inclusive/exclusive query tests\n try:\n float(test_value[0])\n msg = f\"Not testing filters on field {prop} of type {prop_type} containing float values.\"\n self._log.warning(msg)\n return None, msg\n except ValueError:\n pass\n\n if prop_type in (DataType.DICTIONARY,):\n msg = f\"Not testing queries on field {prop} of type {prop_type}.\"\n self._log.warning(msg)\n return None, msg\n\n num_data_returned = {}\n\n inclusive_operators = CONF.inclusive_operators[prop_type]\n exclusive_operators = CONF.exclusive_operators[prop_type]\n field_specific_support_overrides = CONF.field_specific_overrides.get(prop, {})\n\n for operator in inclusive_operators | exclusive_operators:\n # Need to pre-format list and string test values for the query\n _test_value = self._format_test_value(test_value, prop_type, operator)\n\n query_optional = (\n query_optional\n or operator\n in field_specific_support_overrides.get(SupportLevel.OPTIONAL, [])\n )\n\n query = f\"{prop} {operator} {_test_value}\"\n request = f\"{endp}?filter={query}\"\n response, message = self._get_endpoint(\n request,\n multistage=True,\n optional=query_optional,\n expected_status_code=(200, 501),\n )\n\n if response is None or response.status_code != 200:\n if query_optional:\n return (\n None,\n \"Optional query {query!r} raised the error: {message}.\",\n )\n raise ResponseError(\n f\"Unable to perform mandatory query {query!r}, which raised the error: {message}\"\n )\n\n response = response.json()\n\n if \"meta\" not in response or \"more_data_available\" not in response[\"meta\"]:\n raise ResponseError(\n f\"Required field `meta->more_data_available` missing from response for {request}.\"\n )\n\n if not response[\"meta\"][\"more_data_available\"] and \"data\" in response:\n num_data_returned[operator] = len(response[\"data\"])\n else:\n num_data_returned[operator] = response[\"meta\"].get(\"data_returned\")\n\n if prop in CONF.unique_properties and operator == \"=\":\n if num_data_returned[\"=\"] is not None and num_data_returned[\"=\"] == 0:\n raise ResponseError(\n f\"Unable to filter field 'id' for equality, no data was returned for {query}.\"\n )\n if num_data_returned[\"=\"] is not None and num_data_returned[\"=\"] > 1:\n raise ResponseError(\n f\"Filter for an individual 'id' returned {num_data_returned['=']} results, when only 1 was expected.\"\n )\n\n num_response = num_data_returned[operator]\n\n excluded = operator in exclusive_operators\n # if we have all results on this page, check that the blessed ID is in the response\n if excluded and (\n chosen_entry.get(\"id\", \"\")\n in {entry.get(\"id\") for entry in response[\"data\"]}\n ):\n raise ResponseError(\n f\"Entry {chosen_entry['id']} with value {prop!r}: {test_value} was not excluded by {query!r}\"\n )\n\n # check that at least the archetypal structure was returned, unless we are using a fallback value\n if not excluded and not using_fallback:\n if (\n num_data_returned[operator] is not None\n and num_data_returned[operator] < 1\n ):\n raise ResponseError(\n f\"Supposedly inclusive query {query!r} did not include original entry ID {chosen_entry['id']!r} \"\n f\"(with field {prop!r} = {test_value}) potentially indicating a problem with filtering on this field.\"\n )\n\n # check that the filter returned no entries that had a null or missing value for the filtered property\n if any(\n entry.get(\"attributes\", {}).get(prop, entry.get(prop, None)) is None\n for entry in response.get(\"data\", [])\n ):\n raise ResponseError(\n f\"Filter {query!r} on field {prop!r} returned entries that had null or missing values for the field.\"\n )\n\n # Numeric and string comparisons must work both ways...\n if prop_type in (\n DataType.STRING,\n DataType.INTEGER,\n DataType.FLOAT,\n DataType.TIMESTAMP,\n ) and operator not in (\n \"CONTAINS\",\n \"STARTS\",\n \"STARTS WITH\",\n \"ENDS\",\n \"ENDS WITH\",\n ):\n reversed_operator = operator.replace(\"<\", \">\")\n if \"<\" in operator:\n reversed_operator = operator.replace(\"<\", \">\")\n elif \">\" in operator:\n reversed_operator = operator.replace(\">\", \"<\")\n\n # Don't try to reverse string comparison as it is ill-defined\n if prop_type == DataType.STRING and any(\n comp in operator for comp in (\"<\", \">\")\n ):\n continue\n\n reversed_query = f\"{_test_value} {reversed_operator} {prop}\"\n reversed_request = f\"{endp}?filter={reversed_query}\"\n reversed_response, message = self._get_endpoint(\n reversed_request,\n multistage=True,\n optional=query_optional,\n expected_status_code=(200, 501),\n )\n\n if not reversed_response:\n if query_optional:\n return (\n None,\n \"Optional query {reversed_query!r} raised the error: {message}.\",\n )\n raise ResponseError(\n f\"Unable to perform mandatory query {reversed_query!r}, which raised the error: {message}\"\n )\n\n reversed_response = reversed_response.json()\n if (\n \"meta\" not in reversed_response\n or \"more_data_available\" not in reversed_response[\"meta\"]\n ):\n raise ResponseError(\n f\"Required field `meta->more_data_available` missing from response for {request}.\"\n )\n\n if not reversed_response[\"meta\"][\"more_data_available\"]:\n num_reversed_response = len(reversed_response[\"data\"])\n else:\n num_reversed_response = reversed_response[\"meta\"].get(\n \"data_returned\"\n )\n\n if num_response is not None and num_reversed_response is not None:\n if reversed_response[\"meta\"].get(\"data_returned\") != response[\n \"meta\"\n ].get(\"data_returned\"):\n raise ResponseError(\n f\"Query {query} did not work both ways around: {reversed_query}, \"\n \"returning a different number of results each time (as reported by `meta->data_returned`)\"\n )\n\n # check that the filter returned no entries that had a null or missing value for the filtered property\n if any(\n entry.get(\"attributes\", {}).get(prop, entry.get(prop, None)) is None\n for entry in reversed_response.get(\"data\", [])\n ):\n raise ResponseError(\n f\"Filter {reversed_query!r} on field {prop!r} returned entries that had null or missing values for the field.\"\n )\n\n return True, f\"{prop} passed filter tests\"\n\n def _test_info_or_links_endpoint(\n self, request_str: str\n ) -> Union[Literal[False], dict]:\n \"\"\"Requests an info or links endpoint and attempts to deserialize\n the response.\n\n Parameters:\n request_str: The request to make, e.g. \"links\".\n\n Returns:\n `False` if the info response failed deserialization,\n otherwise returns the deserialized object.\n\n \"\"\"\n response, _ = self._get_endpoint(request_str)\n if response:\n deserialized, _ = self._deserialize_response(\n response,\n self._response_classes[request_str],\n request=request_str,\n )\n if deserialized:\n return deserialized.model_dump()\n\n return False\n\n def _test_single_entry_endpoint(self, endp: str) -> None:\n \"\"\"Requests and deserializes a single entry endpoint with the\n appropriate model.\n\n Parameters:\n request_str: The single entry request to make, e.g. \"structures/id_1\".\n\n \"\"\"\n response_cls_name = endp + \"/\"\n if response_cls_name in self._response_classes:\n response_cls = self._response_classes[response_cls_name]\n else:\n self._log.warning(\n \"Deserializing single entry response %s with generic response rather than defined endpoint.\",\n endp,\n )\n response_cls = ValidatorEntryResponseOne\n\n response_fields = set()\n if endp in CONF.entry_schemas:\n response_fields = (\n set(CONF.entry_schemas[endp].keys())\n - CONF.top_level_non_attribute_fields\n )\n\n if endp in self._test_id_by_type:\n test_id = self._test_id_by_type[endp]\n request_str = f\"{endp}/{test_id}\"\n if response_fields:\n request_str += f\"?response_fields={','.join(response_fields)}\"\n response, _ = self._get_endpoint(request_str)\n if response:\n self._test_meta_schema_reporting(response, request_str, optional=True)\n self._deserialize_response(response, response_cls, request=request_str)\n\n def _test_multi_entry_endpoint(self, endp: str) -> None:\n \"\"\"Requests and deserializes a multi-entry endpoint with the\n appropriate model.\n\n Parameters:\n request_str: The multi-entry request to make, e.g.,\n \"structures?filter=nsites<10\"\n\n \"\"\"\n if endp in self._response_classes:\n response_cls = self._response_classes[endp]\n else:\n self._log.warning(\n \"Deserializing multi entry response from %s with generic response rather than defined endpoint.\",\n endp,\n )\n response_cls = ValidatorEntryResponseMany\n\n response_fields = set()\n if endp in CONF.entry_schemas:\n response_fields = (\n set(CONF.entry_schemas[endp].keys())\n - CONF.top_level_non_attribute_fields\n )\n\n request_str = f\"{endp}?page_limit={self.page_limit}\"\n\n if response_fields:\n request_str += f'&response_fields={\",\".join(response_fields)}'\n\n response, _ = self._get_endpoint(request_str)\n\n self._test_if_data_empty(response, request_str, optional=True)\n self._test_meta_schema_reporting(response, request_str, optional=True)\n self._test_page_limit(response)\n\n deserialized, _ = self._deserialize_response(\n response, response_cls, request=request_str\n )\n\n self._get_single_id_from_multi_entry_endpoint(deserialized, request=request_str)\n if deserialized:\n self._test_data_available_matches_data_returned(\n deserialized, request=request_str\n )\n\n @test_case\n def _test_data_available_matches_data_returned(\n self, deserialized: Any\n ) -> tuple[Optional[bool], str]:\n \"\"\"In the case where no query is requested, `data_available`\n must equal `data_returned` in the meta response, which is tested\n here.\n\n Parameters:\n deserialized: The deserialized response to a multi-entry\n endpoint.\n\n Returns:\n `True` if successful, with a string summary.\n\n \"\"\"\n if (\n deserialized.meta.data_available is None\n or deserialized.meta.data_returned is None\n ):\n return (\n None,\n \"`meta->data_available` and/or `meta->data_returned` were not provided.\",\n )\n\n if deserialized.meta.data_available != deserialized.meta.data_returned:\n raise ResponseError(\n f\"No query was performed, but `data_returned` != `data_available` {deserialized.meta.data_returned} vs {deserialized.meta.data_available}.\"\n )\n\n return (\n True,\n \"Meta response contained correct values for data_available and data_returned.\",\n )\n\n def _test_versions_endpoint(self):\n \"\"\"Requests and validate responses for the versions endpoint,\n which MUST exist for unversioned base URLs and MUST NOT exist\n for versioned base URLs.\n\n \"\"\"\n\n # First, check that there is a versions endpoint in the appropriate place:\n # If passed a versioned URL, then strip that version from\n # the URL before looking for `/versions`.\n _old_base_url = self.base_url\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n self.client.base_url = \"/\".join(self.client.base_url.split(\"/\")[:-1])\n self.base_url = self.client.base_url\n\n response, _ = self._get_endpoint(\n CONF.versions_endpoint, expected_status_code=200\n )\n\n if response:\n self._test_versions_endpoint_content(\n response, request=CONF.versions_endpoint\n )\n\n # If passed a versioned URL, first reset the URL of the client to the\n # versioned one, then test that this versioned URL does NOT host a versions endpoint\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n self.client.base_url = _old_base_url\n self.base_url = _old_base_url\n self._get_endpoint(CONF.versions_endpoint, expected_status_code=404)\n\n @test_case\n def _test_versions_endpoint_content(\n self, response: requests.Response\n ) -> tuple[requests.Response, str]:\n \"\"\"Checks that the response from the versions endpoint complies\n with the specification and that its 'Content-Type' header complies with\n [RFC 4180](https://tools.ietf.org/html/rfc4180.html).\n\n Parameters:\n response: The HTTP response from the versions endpoint.\n\n Raises:\n ResponseError: If any content checks fail.\n\n Returns:\n The successful HTTP response or `None`, and a summary string.\n\n \"\"\"\n text_content = response.text.strip().split(\"\\n\")\n if text_content[0] != \"version\":\n raise ResponseError(\n f\"First line of `/{CONF.versions_endpoint}` response must be 'version', not {text_content[0]!r}\"\n )\n\n if len(text_content) <= 1:\n raise ResponseError(\n f\"No version numbers found in `/{CONF.versions_endpoint}` response, only {text_content}\"\n )\n\n for version in text_content[1:]:\n try:\n int(version)\n except ValueError:\n raise ResponseError(\n f\"Version numbers reported by `/{CONF.versions_endpoint}` must be integers specifying the major version, not {text_content}.\"\n )\n\n _content_type = response.headers.get(\"content-type\")\n if not _content_type:\n raise ResponseError(\n \"Missing 'Content-Type' in response header from `/versions`.\"\n )\n\n content_type = [_.replace(\" \", \"\") for _ in _content_type.split(\";\")]\n\n self._test_versions_headers(\n content_type,\n (\"text/csv\", \"text/plain\"),\n optional=True,\n request=CONF.versions_endpoint,\n )\n self._test_versions_headers(\n content_type,\n \"header=present\",\n optional=True,\n request=CONF.versions_endpoint,\n )\n\n return response, \"`/versions` endpoint responded correctly.\"\n\n @test_case\n def _test_versions_headers(\n self,\n content_type: dict[str, Any],\n expected_parameter: Union[str, list[str]],\n ) -> tuple[dict[str, Any], str]:\n \"\"\"Tests that the `Content-Type` field of the `/versions` header contains\n the passed parameter.\n\n Arguments:\n content_type: The 'Content-Type' field from the response of the `/versions` endpoint.\n expected_paramter: A substring or list of substrings that are expected in\n the Content-Type of the response. If multiple strings are passed, they will\n be treated as possible alternatives to one another.\n\n Raises:\n ResponseError: If the expected 'Content-Type' parameter is missing.\n\n Returns:\n The HTTP response headers and a summary string.\n\n \"\"\"\n\n if isinstance(expected_parameter, str):\n expected_parameter = [expected_parameter]\n\n if not any(param in content_type for param in expected_parameter):\n raise ResponseError(\n f\"Incorrect 'Content-Type' header {';'.join(content_type)!r}.\\n\"\n f\"Missing at least one expected parameter(s): {expected_parameter!r}\"\n )\n\n return (\n content_type,\n f\"`/versions` response had one of the expected Content-Type parameters {expected_parameter}.\",\n )\n\n def _test_as_type(self) -> None:\n \"\"\"Tests that the base URL of the validator (i.e. with no\n additional path added) validates with the model selected.\n\n \"\"\"\n response, _ = self._get_endpoint(\"\")\n if response:\n self._log.debug(\"Deserialzing response as type %s\", self.as_type_cls)\n self._deserialize_response(response, self.as_type_cls)\n\n def _test_bad_version_returns_553(self) -> None:\n \"\"\"Tests that a garbage version number responds with a 553\n error code.\n\n \"\"\"\n expected_status_code = [553]\n if re.match(VERSIONS_REGEXP, self.base_url_parsed.path) is not None:\n expected_status_code = [404, 400]\n\n self._get_endpoint(\n \"v123123/info\", expected_status_code=expected_status_code, optional=True\n )\n\n @test_case\n def _test_if_data_empty(\n self,\n response: requests.models.Response,\n request_str: str,\n ):\n \"\"\"Tests whether an endpoint responds a entries under `data`.\"\"\"\n try:\n if not response.json().get(\"data\", []):\n raise ResponseError(\n f\"Query {request_str} did not respond with any entries under `data`. This may not consitute an error, but should be checked.\"\n )\n\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to test presence of `data` for query {request_str}: could not decode response as JSON.\\n{str(response.content)}\"\n )\n\n return (\n True,\n f\"Query {request_str} successfully returned some entries.\",\n )\n\n @test_case\n def _test_meta_schema_reporting(\n self,\n response: requests.models.Response,\n request_str: str,\n ):\n \"\"\"Tests that the endpoint responds with a `meta->schema`.\"\"\"\n try:\n if not response.json().get(\"meta\", {}).get(\"schema\"):\n raise ResponseError(\n f\"Query {request_str} did not report a schema in `meta->schema` field.\"\n )\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to test presence of `meta->schema`: could not decode response as JSON.\\n{str(response.content)}\"\n )\n\n return (\n True,\n f\"Query {request_str} successfully reported a schema in `meta->schema`.\",\n )\n\n @test_case\n def _test_page_limit(\n self,\n response: requests.models.Response,\n check_next_link: int = 5,\n previous_links: Optional[set[str]] = None,\n ) -> tuple[Optional[bool], str]:\n \"\"\"Test that a multi-entry endpoint obeys the page limit by\n following pagination links up to a depth of `check_next_link`.\n\n Parameters:\n response: The response to test for page limit\n compliance.\n check_next_link: Maximum recursion depth for following\n pagination links.\n previous_links: A set of previous links that will be used\n to check that the `next` link is actually new.\n\n Returns:\n `True` if the test was successful and `None` if not, with a\n string summary.\n\n \"\"\"\n if previous_links is None:\n previous_links = set()\n try:\n response_json = response.json()\n except (AttributeError, json.JSONDecodeError):\n raise ResponseError(\"Unable to test endpoint `page_limit` parameter.\")\n\n try:\n num_entries = len(response_json[\"data\"])\n except (KeyError, TypeError):\n raise ResponseError(\n \"Response under `data` field was missing or had wrong type.\"\n )\n\n if num_entries > self.page_limit:\n raise ResponseError(\n f\"Endpoint did not obey page limit: {num_entries} entries vs {self.page_limit} limit\"\n )\n\n try:\n more_data_available = response_json[\"meta\"][\"more_data_available\"]\n except KeyError:\n raise ResponseError(\"Field `meta->more_data_available` was missing.\")\n\n if more_data_available and check_next_link:\n try:\n next_link = response_json[\"links\"][\"next\"]\n if isinstance(next_link, dict):\n next_link = next_link[\"href\"]\n if not next_link:\n raise ResponseError(\n \"Endpoint suggested more data was available but provided no valid links->next link.\"\n )\n except KeyError:\n raise ResponseError(\n \"Endpoint suggested more data was available but provided no valid links->next link.\"\n )\n\n if next_link in previous_links:\n raise ResponseError(\n f\"The next link {next_link} has been provided already for a previous page.\"\n )\n previous_links.add(next_link)\n\n if not isinstance(next_link, str):\n raise ResponseError(\n f\"Unable to parse links->next {next_link!r} as a link.\"\n )\n\n self._log.debug(\"Following pagination link to %r.\", next_link)\n next_response, _ = self._get_endpoint(next_link)\n if not next_response:\n raise ResponseError(\n f\"Error when testing pagination: the response from `links->next` {next_link!r} failed the previous test.\"\n )\n\n check_next_link = check_next_link - 1\n self._test_page_limit(\n next_response,\n check_next_link=check_next_link,\n multistage=bool(check_next_link),\n previous_links=previous_links,\n )\n\n return (\n True,\n f\"Endpoint obeyed page limit of {self.page_limit} by returning {num_entries} entries.\",\n )\n\n @test_case\n def _get_single_id_from_multi_entry_endpoint(self, deserialized):\n \"\"\"Scrape an ID from the multi-entry endpoint to use as query\n for single entry endpoint.\n\n \"\"\"\n if deserialized and deserialized.data:\n self._test_id_by_type[deserialized.data[0].type] = deserialized.data[0].id\n self._log.debug(\n \"Set type %s test ID to %s\",\n deserialized.data[0].type,\n deserialized.data[0].id,\n )\n else:\n return (\n None,\n \"No entries found under endpoint to scrape ID from. \"\n \"This may be caused by previous errors, if e.g. the endpoint failed deserialization.\",\n )\n return (\n self._test_id_by_type[deserialized.data[0].type],\n f\"successfully scraped test ID from {deserialized.data[0].type} endpoint\",\n )\n\n @test_case\n def _deserialize_response(\n self,\n response: requests.models.Response,\n response_cls: Any,\n request: Optional[str] = None,\n ) -> tuple[Any, str]:\n \"\"\"Try to create the appropriate pydantic model from the response.\n\n Parameters:\n response: The response to try to deserialize.\n response_cls: The class to use for deserialization.\n request: Optional string that will be displayed as the attempted\n request in the validator output.\n\n Returns:\n The deserialized object (or `None` if unsuccessful) and a\n human-readable summary\n\n \"\"\"\n if not response:\n raise ResponseError(\"Request failed\")\n try:\n json_response = response.json()\n except json.JSONDecodeError:\n raise ResponseError(\n f\"Unable to decode response as JSON. Response: {response}\"\n )\n\n self._log.debug(\n f\"Deserializing {json.dumps(json_response, indent=2)} as model {response_cls}\"\n )\n\n return (\n response_cls(**json_response),\n f\"deserialized correctly as object of type {response_cls}\",\n )\n\n @test_case\n def _get_available_endpoints(\n self, base_info: Union[Any, dict[str, Any]]\n ) -> tuple[Optional[list[str]], str]:\n \"\"\"Tries to get `entry_types_by_format` from base info response\n even if it could not be deserialized.\n\n Parameters:\n base_info: Either the unvalidated JSON representation of the\n base info, or the deserialized object.\n\n Returns:\n The list of JSON entry endpoints (or `None` if unavailable)\n and a string summary.\n\n \"\"\"\n available_json_entry_endpoints = []\n for _ in [0]:\n try:\n available_json_entry_endpoints = base_info[\"data\"][\"attributes\"][\n \"entry_types_by_format\"\n ][\"json\"]\n break\n except (KeyError, TypeError):\n raise ResponseError(\n \"Unable to get entry_types_by_format from unserializable base info response {}.\".format(\n base_info\n )\n )\n else:\n raise ResponseError(\n \"Unable to find any JSON entry types in entry_types_by_format\"\n )\n\n if self.index and available_json_entry_endpoints != []:\n raise ResponseError(\n \"No entry endpoint are allowed for an Index meta-database\"\n )\n\n for non_entry_endpoint in CONF.non_entry_endpoints:\n if non_entry_endpoint in available_json_entry_endpoints:\n raise ResponseError(\n f'Illegal entry \"{non_entry_endpoint}\" was found in entry_types_by_format\"'\n )\n\n # Filter out custom extension endpoints that are not covered in the specification\n available_json_entry_endpoints = [\n endp\n for endp in available_json_entry_endpoints\n if endp in CONF.entry_endpoints\n ]\n\n return (\n available_json_entry_endpoints,\n \"successfully found available entry types in baseinfo\",\n )\n\n @test_case\n def _get_endpoint(\n self, request_str: str, expected_status_code: Union[list[int], int] = 200\n ) -> tuple[Optional[requests.Response], str]:\n \"\"\"Gets the response from the endpoint specified by `request_str`.\n function is wrapped by the `test_case` decorator\n\n Parameters:\n request_str: The request to make to the client.\n expected_status_code: If the request responds with a different\n status code to this one, raise a ResponseError.\n\n Returns:\n The response to the request (if successful) or `None`, plus\n a string summary.\n\n \"\"\"\n request_str = request_str.replace(\"\\n\", \"\")\n response = self.client.get(request_str)\n\n if isinstance(expected_status_code, int):\n expected_status_code = [expected_status_code]\n\n message = f\"received expected response: {response}.\"\n\n if response.status_code != 200:\n message = f\"Request to '{request_str}' returned HTTP status code {response.status_code}.\"\n message += \"\\nAdditional details from implementation:\"\n try:\n for error in response.json().get(\"errors\", []):\n message += f'\\n {error.get(\"title\", \"N/A\")}: {error.get(\"detail\", \"N/A\")} ({error.get(\"source\", {}).get(\"pointer\", \"N/A\")})'\n except json.JSONDecodeError:\n message += f\"\\n Could not parse response as JSON. Content type was {response.headers.get('content-type')!r}.\"\n\n if response.status_code not in expected_status_code:\n raise ResponseError(message)\n\n return response, message\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.__init__","title":"__init__(client=None, base_url=None, verbosity=0, respond_json=False, page_limit=4, max_retries=5, run_optional_tests=True, fail_fast=False, as_type=None, index=False, minimal=False, http_headers=None, timeout=DEFAULT_CONN_TIMEOUT, read_timeout=DEFAULT_READ_TIMEOUT)
","text":"Set up the tests to run, based on constants in this module for required endpoints.
Parameters:
Name Type Description Defaultclient
Optional[Any]
A client that has a .get()
method to obtain the response from the implementation. If None
, then Client
will be used.
None
base_url
Optional[str]
The URL of the implementation to validate. Unless performing \"as_type\" validation, this should point to the base of the OPTIMADE implementation.
None
verbosity
int
The verbosity of the output and logging as an integer (0
: critical, 1
: warning, 2
: info, 3
: debug).
0
respond_json
bool
If True
, print only a JSON representation of the results of validation to stdout.
False
page_limit
int
The default page limit to apply to filters.
4
max_retries
int
Argument is passed to the client for how many attempts to make for a request before failing.
5
run_optional_tests
bool
Whether to run the tests on optional OPTIMADE features.
True
fail_fast
bool
Whether to exit validation after the first failure of a mandatory test.
False
as_type
Optional[str]
An OPTIMADE entry or endpoint type to coerce the response from implementation into, e.g. \"structures\". Requires base_url
to be pointed to the corresponding endpoint.
None
index
bool
Whether to validate the implementation as an index meta-database.
False
minimal
bool
Whether or not to run only a minimal test set.
False
http_headers
Optional[dict[str, str]]
Dictionary of additional headers to add to every request.
None
timeout
float
The connection timeout to use for all requests (in seconds).
DEFAULT_CONN_TIMEOUT
read_timeout
float
The read timeout to use for all requests (in seconds).
DEFAULT_READ_TIMEOUT
Source code in optimade/validator/validator.py
def __init__(\n self,\n client: Optional[Any] = None,\n base_url: Optional[str] = None,\n verbosity: int = 0,\n respond_json: bool = False,\n page_limit: int = 4,\n max_retries: int = 5,\n run_optional_tests: bool = True,\n fail_fast: bool = False,\n as_type: Optional[str] = None,\n index: bool = False,\n minimal: bool = False,\n http_headers: Optional[dict[str, str]] = None,\n timeout: float = DEFAULT_CONN_TIMEOUT,\n read_timeout: float = DEFAULT_READ_TIMEOUT,\n):\n \"\"\"Set up the tests to run, based on constants in this module\n for required endpoints.\n\n Arguments:\n client: A client that has a `.get()` method to obtain the\n response from the implementation. If `None`, then\n [`Client`][optimade.validator.utils.Client] will be used.\n base_url: The URL of the implementation to validate. Unless\n performing \"as_type\" validation, this should point to the\n base of the OPTIMADE implementation.\n verbosity: The verbosity of the output and logging as an integer\n (`0`: critical, `1`: warning, `2`: info, `3`: debug).\n respond_json: If `True`, print only a JSON representation of the\n results of validation to stdout.\n page_limit: The default page limit to apply to filters.\n max_retries: Argument is passed to the client for how many\n attempts to make for a request before failing.\n run_optional_tests: Whether to run the tests on optional\n OPTIMADE features.\n fail_fast: Whether to exit validation after the first failure\n of a mandatory test.\n as_type: An OPTIMADE entry or endpoint type to coerce the response\n from implementation into, e.g. \"structures\". Requires `base_url`\n to be pointed to the corresponding endpoint.\n index: Whether to validate the implementation as an index meta-database.\n minimal: Whether or not to run only a minimal test set.\n http_headers: Dictionary of additional headers to add to every request.\n timeout: The connection timeout to use for all requests (in seconds).\n read_timeout: The read timeout to use for all requests (in seconds).\n\n \"\"\"\n self.verbosity = verbosity\n self.max_retries = max_retries\n self.page_limit = page_limit\n self.index = index\n self.run_optional_tests = run_optional_tests\n self.fail_fast = fail_fast\n self.respond_json = respond_json\n self.minimal = minimal\n\n if as_type is None:\n self.as_type_cls = None\n elif self.index:\n if as_type not in CONF.response_classes_index:\n raise RuntimeError(\n f\"Provided as_type='{as_type}' not allowed for an Index meta-database.\"\n )\n self.as_type_cls = CONF.response_classes_index[as_type]\n elif as_type in (\"structure\", \"reference\"):\n self.as_type_cls = CONF.response_classes[f\"{as_type}s/\"]\n else:\n self.as_type_cls = CONF.response_classes[as_type]\n\n if client is None and base_url is None:\n raise RuntimeError(\n \"Need at least a URL or a client to initialize validator.\"\n )\n if base_url and client:\n raise RuntimeError(\"Please specify at most one of base_url or client.\")\n if client:\n self.client = client\n self.base_url = self.client.base_url\n # If a custom client has been provided, try to set custom headers if they have been specified,\n # but do not overwrite any existing attributes\n if http_headers:\n if not hasattr(self.client, \"headers\"):\n self.client.headers = http_headers\n else:\n print_warning(\n f\"Not using specified request headers {http_headers} with custom client {self.client}.\"\n )\n else:\n while base_url.endswith(\"/\"): # type: ignore[union-attr]\n base_url = base_url[:-1] # type: ignore[index]\n self.base_url = base_url\n self.client = Client(\n self.base_url, # type: ignore[arg-type]\n max_retries=self.max_retries,\n headers=http_headers,\n timeout=timeout,\n read_timeout=read_timeout,\n )\n\n self._setup_log()\n\n self._response_classes = (\n CONF.response_classes_index if self.index else CONF.response_classes\n )\n\n # some simple checks on base_url\n self.base_url = str(self.base_url)\n self.base_url_parsed = urllib.parse.urlparse(self.base_url)\n # only allow filters/endpoints if we are working in \"as_type\" mode\n if self.as_type_cls is None and self.base_url_parsed.query:\n raise SystemExit(\n f\"Base URL {self.base_url} not appropriate: should not contain a filter.\"\n )\n\n self.valid = None\n\n self._test_id_by_type: dict[str, Any] = {}\n self._entry_info_by_type: dict[str, Any] = {}\n\n self.results = ValidatorResults(verbosity=self.verbosity)\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.print_summary","title":"print_summary()
","text":"Print a summary of the results of validation.
Source code inoptimade/validator/validator.py
def print_summary(self):\n \"\"\"Print a summary of the results of validation.\"\"\"\n if self.respond_json:\n print(json.dumps(dataclasses.asdict(self.results), indent=2))\n return\n\n if self.results.failure_messages:\n print(\"\\n\\nFAILURES\")\n print(\"========\\n\")\n for message in self.results.failure_messages:\n print_failure(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.optional_failure_messages:\n print(\"\\n\\nOPTIONAL TEST FAILURES\")\n print(\"======================\\n\")\n for message in self.results.optional_failure_messages:\n print_notify(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.results.internal_failure_messages:\n print(\"\\n\\nINTERNAL FAILURES\")\n print(\"=================\\n\")\n print(\n \"There were internal validator failures associated with this run.\\n\"\n \"If this problem persists, please report it at:\\n\"\n \"https://github.com/Materials-Consortia/optimade-python-tools/issues/new\\n\"\n )\n\n for message in self.results.internal_failure_messages:\n print_warning(message[0])\n for line in message[1].split(\"\\n\"):\n print_warning(\"\\t\" + line)\n\n if self.valid or (not self.valid and not self.fail_fast):\n final_message = f\"\\n\\nPassed {self.results.success_count} out of {self.results.success_count + self.results.failure_count + self.results.internal_failure_count} tests.\"\n if not self.valid:\n print_failure(final_message)\n else:\n print_success(final_message)\n\n if self.run_optional_tests and not self.fail_fast:\n print(\n f\"Additionally passed {self.results.optional_success_count} out of \"\n f\"{self.results.optional_success_count + self.results.optional_failure_count} optional tests.\"\n )\n
"},{"location":"api_reference/validator/validator/#optimade.validator.validator.ImplementationValidator.validate_implementation","title":"validate_implementation()
","text":"Run all the test cases on the implementation, or the single type test, depending on what options were provided on initialiation.
Sets the self.valid
attribute to True
or False
depending on the outcome of the tests.
Raises:
Type DescriptionRuntimeError
If it was not possible to start the validation process.
Source code inoptimade/validator/validator.py
def validate_implementation(self):\n \"\"\"Run all the test cases on the implementation, or the single type test,\n depending on what options were provided on initialiation.\n\n Sets the `self.valid` attribute to `True` or `False` depending on the\n outcome of the tests.\n\n Raises:\n RuntimeError: If it was not possible to start the validation process.\n\n \"\"\"\n # If a single \"as type\" has been set, only run that test\n if self.as_type_cls is not None:\n self._log.debug(\n \"Validating response of %s with model %s\",\n self.base_url,\n self.as_type_cls,\n )\n self._test_as_type()\n self.valid = not bool(self.results.failure_count)\n self.print_summary()\n return\n\n # Test entire implementation\n if self.verbosity >= 0:\n print(f\"Testing entire implementation at {self.base_url}\")\n info_endp = CONF.info_endpoint\n self._log.debug(\"Testing base info endpoint of %s\", info_endp)\n\n # Get and validate base info to find endpoints\n # If this is not possible, then exit at this stage\n base_info = self._test_info_or_links_endpoint(info_endp)\n if not base_info:\n self._log.critical(\n f\"Unable to deserialize response from introspective {info_endp!r} endpoint. \"\n \"This is required for all further validation, so the validator will now exit.\"\n )\n # Set valid to False to ensure error code 1 is raised at CLI\n self.valid = False\n self.print_summary()\n return\n\n # Grab the provider prefix from base info and use it when looking for provider fields\n self.provider_prefix = None\n meta = base_info.get(\"meta\", {})\n if meta.get(\"provider\") is not None:\n self.provider_prefix = meta[\"provider\"].get(\"prefix\")\n\n # Set the response class for all `/info/entry` endpoints based on `/info` response\n self.available_json_endpoints, _ = self._get_available_endpoints(\n base_info, request=info_endp\n )\n for endp in self.available_json_endpoints:\n self._response_classes[f\"{info_endp}/{endp}\"] = EntryInfoResponse\n\n # Run some tests on the versions endpoint\n self._log.debug(\"Testing versions endpoint %s\", CONF.versions_endpoint)\n self._test_versions_endpoint()\n self._test_bad_version_returns_553()\n\n # Test that entry info endpoints deserialize correctly\n # If they do not, the corresponding entry in _entry_info_by_type\n # is set to False, which must be checked for further validation\n for endp in self.available_json_endpoints:\n entry_info_endpoint = f\"{info_endp}/{endp}\"\n self._log.debug(\"Testing expected info endpoint %s\", entry_info_endpoint)\n self._entry_info_by_type[endp] = self._test_info_or_links_endpoint(\n entry_info_endpoint\n )\n\n # Test that the results from multi-entry-endpoints obey, e.g. page limits,\n # and that all entries can be deserialized with the patched models.\n # These methods also set the test_ids for each type of entry, which are validated\n # in the next loop.\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing multiple entry endpoint of %s\", endp)\n self._test_multi_entry_endpoint(endp)\n\n # Test that the single IDs scraped earlier work with the single entry endpoint\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing single entry request of type %s\", endp)\n self._test_single_entry_endpoint(endp)\n\n # Use the _entry_info_by_type to construct filters on the relevant endpoints\n if not self.minimal:\n for endp in self.available_json_endpoints:\n self._log.debug(\"Testing queries on JSON entry endpoint of %s\", endp)\n self._recurse_through_endpoint(endp)\n\n # Test that the links endpoint can be serialized correctly\n self._log.debug(\"Testing %s endpoint\", CONF.links_endpoint)\n self._test_info_or_links_endpoint(CONF.links_endpoint)\n\n self.valid = not (\n self.results.failure_count or self.results.internal_failure_count\n )\n\n self.print_summary()\n
"},{"location":"concepts/filtering/","title":"Filter parsing and transforming","text":"One of the aims of this package is to integrate with existing databases and APIs, and as such your particular backend may not have a supported filter transformer. This guide will briefly outline how to parse OPTIMADE filter strings into database or API-specific queries.
"},{"location":"concepts/filtering/#parsing-optimade-filter-strings","title":"Parsing OPTIMADE filter strings","text":"The LarkParser
class will take an OPTIMADE filter string, supplied by the user, and parse it into a lark.Tree
instance.
Example use:
from optimade.filterparser import LarkParser\n\np = LarkParser(version=(1, 0, 0))\ntree = p.parse(\"nelements<3\")\nprint(tree)\n
Tree('filter', [Tree('expression', [Tree('expression_clause', [Tree('expression_phrase', [Tree('comparison', [Tree('property_first_comparison', [Tree('property', [Token('IDENTIFIER', 'nelements')]), Tree('value_op_rhs', [Token('OPERATOR', '<'), Tree('value', [Tree('number', [Token('SIGNED_INT', '3')])])])])])])])])])\n
print(tree.pretty())\n
filter\n expression\n expression_clause\n expression_phrase\n comparison\n property_first_comparison\n property nelements\n value_op_rhs\n <\n value\n number 3\n
tree = p.parse('_mp_bandgap > 5.0 AND _cod_molecular_weight < 350')\nprint(tree.pretty())\n
filter\n expression\n expression_clause\n expression_phrase\n comparison\n property_first_comparison\n property _mp_bandgap\n value_op_rhs\n >\n value\n number 5.0\n expression_phrase\n comparison\n property_first_comparison\n property _cod_molecular_weight\n value_op_rhs\n <\n value\n number 350\n
"},{"location":"concepts/filtering/#flow-for-parsing-a-user-supplied-filter-and-converting-to-a-backend-query","title":"Flow for parsing a user-supplied filter and converting to a backend query","text":"After the LarkParser
has turned the filter string into a lark.Tree
, it is fed to a lark.Transformer
instance, which transforms the 'lark.Tree' into a backend-specific representation of the query. For example, MongoTransformer
will turn the tree into something useful for a MongoDB backend:
# Example: Converting to MongoDB Query Syntax\nfrom optimade.filtertransformers.mongo import MongoTransformer\n\ntransformer = MongoTransformer()\n\ntree = p.parse('_mp_bandgap > 5.0 AND _cod_molecular_weight < 350')\nquery = transformer.transform(tree)\nprint(query)\n
{\n \"$and\": [\n {\"_mp_bandgap\": {\"$gt\": 5.0}},\n {\"_cod_molecular_weight\": {\"$lt\": 350.0}}\n ]\n}\n
"},{"location":"concepts/filtering/#developing-new-filter-transformers","title":"Developing new filter transformers","text":"In order to support a new backend, you will need to create a new filter transformer that inherits from the BaseTransformer
. This transformer will need to override the methods that match the particular grammatical constructs in the Lark grammar in order to construct a query. Two examples can be found within optimade-python-tools
, one for MongoDB (MongoTransformer
) and one for Elasticsearch (ElasticTransformer
).
In some cases, you may also need to extend the base EntryCollection
, the class that receives the transformed filter as an argument to its private ._run_db_query()
method. This class handles the connections to the underlying database, formatting of the response in an OPTIMADE format, and other API features such as sorting and pagination. Again, the examples for MongoDB (MongoCollection
) and Elasticsearch (ElasticCollection
) should be helpful.
If you would like to contribute your new filter transformer back to the package, please raise an issue to signal your intent (in case someone else is already working on this). Adding a transformer requires the following:
.py
file) in the optimade/filtertransformers
folder containing an implementation of the transformer object that extends optimade.filtertransformers.base_transformer.BaseTransformer
.extra_requires
\" entry in setup.py
and in the requirements.txt
file.optimade/filtertransformers/tests
that are skipped if the required packages fail to import.optimade-python-tools
contains tools for validating external OPTIMADE implementations that may be helpful for all OPTIMADE providers. The validator is dynamic and fuzzy, in that the tested filters are generated based on random entries served by the API, and the description of the API provided at the /info
endpoint.
The validator is implemented in the optimade.validator
submodule, but the two main entry points are:
optimade-validator
script, which is installed alongside the package.optimade-validator-action
which allows the validator to be used as a GitHub Action.To run the script, simply provide an OPTIMADE URL to the script at the command-line. You can use the following to validate the Fly deployment of our reference server:
$ optimade-validator https://optimade.fly.dev/\nTesting entire implementation at https://optimade.fly.dev\n...\n
Several additional options can be found under the --help
flag, with the most important being -v/-vvvv
to set the verbosity, --index
to validate OPTIMADE index meta-databases and --json
to receive the validation results as JSON document for programmatic use.
$ optimade-validator --help\nusage: optimade-validator [-h] [-v] [-j] [-t AS_TYPE] [--index]\n [--skip-optional] [--fail-fast] [-m]\n [--page_limit PAGE_LIMIT]\n [--headers HEADERS]\n [base_url]\n\nTests OPTIMADE implementations for compliance with the optimade-python-tools models.\n\n- To test an entire implementation (at say example.com/optimade/v1) for all required/available endpoints:\n\n $ optimade-validator http://example.com/optimade/v1\n\n- To test a particular response of an implementation against a particular model:\n\n $ optimade-validator http://example.com/optimade/v1/structures/id=1234 --as-type structure\n\n- To test a particular response of an implementation against a particular model:\n\n $ optimade-validator http://example.com/optimade/v1/structures --as-type structures\n\n\npositional arguments:\n base_url The base URL of the OPTIMADE\n implementation to point at, e.g.\n 'http://example.com/optimade/v1' or\n 'http://localhost:5000/v1'\n\noptional arguments:\n -h, --help show this help message and exit\n -v, --verbosity Increase the verbosity of the output.\n (-v: warning, -vv: info, -vvv: debug)\n -j, --json Only a JSON summary of the validator\n results will be printed to stdout.\n -t AS_TYPE, --as-type AS_TYPE\n Validate the request URL with the\n provided type, rather than scanning the\n entire implementation e.g. optimade-\n validator `http://example.com/optimade/v1\n /structures/0 --as-type structure`\n --index Flag for whether the specified OPTIMADE\n implementation is an Index meta-database\n or not.\n --skip-optional Flag for whether the skip the tests of\n optional features.\n --fail-fast Whether to exit on first test failure.\n -m, --minimal Run only a minimal test set.\n --page_limit PAGE_LIMIT\n Alter the requested page limit for some\n tests.\n --headers HEADERS Additional HTTP headers to use for each\n request, specified as a JSON object.\n
"},{"location":"deployment/container/","title":"Run in a container (Docker)","text":""},{"location":"deployment/container/#retrieve-the-container-image","title":"Retrieve the container image","text":"Retrieving the container image is explained in the installation instructions. In short, using Docker you can run:
docker pull ghcr.io/materials-consortia/optimade\n
A general overview on running a container is also given in the installations instructions.
"},{"location":"deployment/container/#prepare-the-container-and-configure-the-server","title":"Prepare the container and configure the server","text":"Tip
A more complete overview of configuring the OPTIMADE server can be seen in the configuration section.
By default, the server will use the test configuration, including test data for structures and references and an in-memory mongomock database backend. This can be changed in several different ways.
One is to git clone
the repository locally and bind the repository folder to the /app
folder in the container:
# volume will bind the local version of `optimade-python-tools` to the container.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --volume /path/to/optimade-python-tools:/app \\\n ghcr.io/materials-consortia/optimade:latest\n
Help
To clone the repository you can run:
git clone --recursive https://github.com/Materials-Consortia/optimade-python-tools.git\n
You should change the /path/to/optimade-python-tools
to the full local path to the repository - or use $PWD
if you are running this command from the root of the cloned repository on a Unix system. Equivalently, %cd%
should work on Windows.
In this setup you can change the repository root file optimade_config.json
with the appropriate information. E.g., if you do not wish to use the test data you can add the insert_test_data
key with a value of false
.
Another option is to not git clone
the repository locally, but instead only generate a JSON or YAML file somewhere, where the directory of its location is bound to another location in the container that is not used, e.g., /config
or similar. As an example, let us say we generate config.yml
locally, with the full path /home/user/optimade/config.yml
. Then we can start the server with the following command:
# volume will bind the local directory to an unused folder in the container.\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --volume /home/user/optimade:/config \\\n --env OPTIMADE_CONFIG_FILE=/config/config.yml \\\n ghcr.io/materials-consortia/optimade:latest\n
As shown, it is necessary to update the environment variable OPTIMADE_CONFIG_FILE
within the container to point to the new internal path to the config file. By default, this environment variable points to /app/optimade_config.json
.
This also reveals another way of configuring the server: set environment variables when running the container to supply values that would otherwise be supplied from the configuration file.
The docker run
command even has the opportunity to pass a path to a file containing a list of environment variables (--env-file /path/to/env_file
), if you wish to configure the server in this way instead of through the standard configuration file.
In this example we want to setup various services through containers that interact with each other on a closed internal network, only the OPTIMADE servers are exposed to the \"outside\".
For this example we will use Docker only. One could choose to use the Docker Compose framework to neatly express the services in a single YAML file, however, to keep this compatible with Docker alternatives, we will focus on only using Docker.
We will also setup an index meta-database, with an associated OPTIMADE server, serving data from a MongoDB backend. Hence, we need to run three separate services, configure it properly, and make sure only the OPTIMADE APIs are exposed.
"},{"location":"deployment/container/#1-setup-closed-network","title":"1. Setup closed network","text":"First, we want to create the internal network:
docker network create -d bridge optimade\n
"},{"location":"deployment/container/#2-run-mongodb-service","title":"2. Run MongoDB service","text":"Then, we can create a volume for the MongoDB server to use and start it.
Note
This can be setup in other ways, binding to local paths or otherwise. For more information on the specifics of how to use the MongoDB container image, see the Docker Hub documentation. For more information from the MongoDB team concerning the Enterprise version, see the MongoDB documentation.
docker volume create mongodb-persist\n# `mongo` will be the host name in the docker network, using `--name`.\ndocker run \\\n --detach \\\n --name mongo \\\n --volume mongodb-persist:/data/db \\\n --network optimade \\\n docker.io/library/mongo:latest\n
At this point you should fill up the database with your data, or instead of doing the command above exactly, you could choose to bind to another \"local\" path that contains an existing MongoDB you have.
Most likely, you will have a database dump archive, which you will now be able to import. If you do so, please note the name of the database, the collection, and consider any data model mappings from your data to OPTIMADE data models for structures and/or references.
In the example, we assume the database has been filled with fully valid OPTIMADE structures and references in a database called optimade_prod, within the collections structures and references, respectively.
"},{"location":"deployment/container/#3-run-the-optimade-service","title":"3. Run the OPTIMADE service","text":"With this information, we can now start the OPTIMADE server:
docker run \\\n --rm \\\n --detach \\\n --publish 8081:5000 \\\n --env MAIN=main \\\n --name my-optimade \\\n --network optimade \\\n --env OPTIMADE_CONFIG_FILE= \\\n --env optimade_insert_test_data=false \\\n --env optimade_database_backend=mongodb \\\n --env optimade_mongo_uri=mongodb://mongo:27017 \\\n --env optimade_mongo_database=optimade_prod \\\n --env optimade_references_collection=references \\\n --env optimade_structures_collection=structures \\\n --env optimade_page_limit=25 \\\n --env optimade_page_limit_max=100 \\\n --env optimade_base_url=http://localhost:8081 \\\n --env optimade_index_base_url=http://localhost:8080 \\\n --env optimade_provider=\"{\\\"prefix\\\":\\\"myorg\\\",\\\"name\\\":\\\"My Organization\\\",\\\"description\\\":\\\"Short description for My Organization\\\",\\\"homepage\\\":\\\"https://example.org\\\"}\" \\\n ghcr.io/materials-consortia/optimade:latest\n
Note, the optimade_base_url
and optimade_index_base_url
values should be different if the server is run through a reverse-proxy service like NGINX, Apache or other. For this example, we are only concerned with exposing the OPTIMADE APIs from the localhost
.
Furthermore, note that we \"unset\" the OPTIMADE_CONFIG_FILE
environment variable to ensure the system defaults are used instead of configuration values that matches the test data.
The next step is to start the OPTIMADE index meta-database. For this, we will first create a JSON file to reference and load as data for the /links
endpoint:
[\n {\n \"id\": \"my-optimade-db\",\n \"type\": \"links\",\n \"name\": \"My OPTIMADE API\",\n \"description\": \"An OPTIMADE API for my database of essential material structures.\",\n \"base_url\": \"http://localhost:8081\",\n \"homepage\": \"https://example.org\",\n \"link_type\": \"child\"\n }\n]\n
This file is stored in a newly created directory at /home/user/optimade/index_data/index_links.json
.
Now, we can start the index meta-database server:
# `optimade_insert_test_data` needs to be `true` to insert JSON file data\ndocker run \\\n --rm \\\n --detach \\\n --publish 8080:5000 \\\n --env MAIN=main_index \\\n --name my-optimade-index \\\n --network optimade \\\n --env OPTIMADE_CONFIG_FILE= \\\n --env optimade_insert_test_data=true \\\n --env optimade_database_backend=mongodb \\\n --env optimade_mongo_uri=mongodb://mongo:27017 \\\n --env optimade_mongo_database=optimade_index_prod \\\n --env optimade_links_collection=links \\\n --env optimade_page_limit=25 \\\n --env optimade_page_limit_max=100 \\\n --env optimade_base_url=http://localhost:8080 \\\n --env optimade_index_base_url=http://localhost:8080 \\\n --env optimade_provider=\"{\\\"prefix\\\":\\\"myorg\\\",\\\"name\\\":\\\"My Organization\\\",\\\"description\\\":\\\"Short description for My Organization\\\",\\\"homepage\\\":\\\"https://example.org\\\"}\" \\\n --env optimade_default_db=my-optimade-db \\\n --env optimade_index_links_path=/external/index_data/index_links.json \\\n --volume /home/user/optimade/index_data:/external/index_data \\\n ghcr.io/materials-consortia/optimade:latest\n
"},{"location":"deployment/container/#5-test-the-services","title":"5. Test the services","text":"Finally, we can test it all works as intended.
Go to localhost:8080/links to check you see the list of OPTIMADE databases linked to by the index meta-database. This list should include the entry from the JSON file above with ID my-optimade-db
.
You could from there go to localhost:8081/info and see the introspective information about the OPTIMADE database. Furthermore, now you could go search through the material structures in your database, e.g., retrieving all structures that include carbon and oxygen in their list of chemical elements: localhost:8081/structures?filter=elements HAS ALL \"C\",\"O\".
It is also good to test the limits we specified are upheld, e.g., the maximum number of requested entries is not allowed to exceed 100 according to the optimade_page_limit_max
value we have specified. To test this, we could request a response with 101 entries, which should return an error: localhost:8081/structures?page_limit=101. And then a request for a response with 101 entries, which should not return an error: localhost:8081/structures?page_limit=100.
If you wish to inspect the logs of any service, you can use the docker logs
command, followed by any of the service container names, e.g., docker logs my-optimade
will display the logged stdout
and stderr
from the OPTIMADE database server.
Use the test data
If you do not have any data with which to fill up the MongoDB backend, you can run through the example using test data with some minor changes. However, it is crucial you first git clone
the repository locally, since the test data is included only in the repository - not the container image.
Run all docker
commands from the root of the cloned repository.
When running the OPTIMADE servers, leave out the line concerning \"unsetting\" the OPTIMADE_CONFIG_FILE
environment variable. When first running the OPTIMADE server (not the index meta-database) set the optimade_insert_test_data
value to true
. If you stop the server and want to restart it, you should then set the variable to false
, since the startup of the server will otherwise fail, due to the test data already existing in the MongoDB database collections and the subsequent re-insertion will throw an error.
The optimade
package can be used to create a standalone web application that serves the OPTIMADE API based on a pre-configured MongoDB backend. In this document, we are going to use optimade
differently and use it to add an OPTIMADE API implementation alongside an existing API that employs an Elasticsearch storage layer.
Let's assume we already have a FastAPI application that runs an unrelated web service, and that we use an Elasticsearch backend that contains all structure data, but not necessarily in a form that OPTIMADE expects.
"},{"location":"deployment/integrated/#providing-the-optimade-configuration","title":"Providing theoptimade
configuration","text":"optimade
can read its configuration from a JSON file. It uses the OPTIMADE_CONFIG_FILE
environment variable (or a default path) to find the config file. If you run optimade
code inside another application, you might want to provide this config file as part of the source code and not via environment variables. Let's say you have a file optimade_config.json
as part of the Python module that you use to create your OPTIMADE API.
Tip
You can find more detailed information about configuring the optimade
server in the Configuration section.
Before importing any optimade
modules, you can set the OPTIMADE_CONFIG_FILE
environment variable to refer to your config file:
import os\nfrom pathlib import Path\n\nos.environ['OPTIMADE_CONFIG_FILE'] = str(Path(__file__).parent / \"optimade_config.json\")\n
"},{"location":"deployment/integrated/#customize-the-entrycollection-implementation","title":"Customize the EntryCollection
implementation","text":"Let's assume that your Elasticsearch backend stores structure data in a different enough manner that you need to provide your own custom implementation. The following code customizes the EntryCollection
class for structures, whilst keeping the default MongoDB-based implementation (using MongoCollection
) for all other entry types.
from optimade.server.routers import structures\n\nstructures.structures_coll = MyElasticsearchStructureCollection()\n
You can imagine that MyElasticsearchStructureCollection
either sub-classes the default optimade
Elasticsearch implementation (ElasticsearchCollection
) or sub-classes EntryCollection
, depending on how deeply you need to customize the default optimade
behavior.
Let's assume you have an existing FastAPI app my_app
. It already implements a few routers under certain path prefixes, and now you want to add an OPTIMADE implementation under the path prefix /optimade
.
First, you have to set the root_path
in the optimade
configuration, so that the app expects all requests to be prefixed with /optimade
.
Second, you simply mount the optimade
app into your existing app my_app
:
from optimade.server.config import CONFIG\n\nCONFIG.root_path = \"/optimade\"\n\nfrom optimade.server import main as optimade\n\noptimade.add_major_version_base_url(optimade.app)\nmy_app.mount(\"/optimade\", optimade.app)\n
Tip
In the example above, we imported CONFIG
before main
so that our config was loaded before app creation. To avoid the need for this, the root_path
can be set in your JSON config file, passed as an environment variable, or declared in a custom Python module (see Configuration).
See also the FastAPI documentation on sub-applications.
Now, if you run my_app
, it will still serve all its routers as before and in addition it will also serve all OPTIMADE routes under /optimade/
and the versioned URLs /optimade/v1/
.
This package includes a Python client that can be used to query multiple OPTIMADE APIs simultaneously, whilst automatically paginating through the results of each query. The client can be used from the command-line (optimade-get
), or called in Python code.
The client does not currently validate the returned data it comes back from the databases, and as some OPTIMADE APIs do not implement all features, it is worth paying attention to any error messages emitted by each database.
"},{"location":"getting_started/client/#features","title":"Features","text":"This list outlines the current and planned features for the client:
The client requires some extra dependencies that can be installed with the PyPI package with
pip install optimade[http_client]\n
or from a local copy of the repository with pip install -e .[http_client]\n
"},{"location":"getting_started/client/#usage","title":"Usage","text":"By default, the client will query all OPTIMADE API URLs that it can find via the Providers list:
Command linePythonoptimade-get --filter 'elements HAS \"Ag\"'\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nresults = client.get('elements HAS \"Ag\"')\n
At the command line, it may be immediately useful to redirect or save these results to a file:
# Save the results to a JSON file directly\noptimade-get --filter 'elements HAS \"Ag\"' --output-file results.json\n# or redirect the results (in a POSIX shell)\noptimade-get --filter 'elements HAS \"Ag\"' > results.json\n
We can refine the search by manually specifying some URLs:
Command linePythonoptimade-get --output-file results.json https://optimade.fly.dev https://optimade.odbx.science\n
from optimade.client import OptimadeClient\nclient = OptimadeClient(\n base_urls=[\"https://optimade.fly.dev\", \"https://optimade.odbx.science\"]\n)\nclient.get()\n
or by including/excluding some providers by their registered IDs in the Providers list.
Query only a list of included providers (after a lookup of the providers list):
Command linePython# Only query databases served by the example providers\noptimade-get --include-providers exmpl,optimade\n
# Only query databases served by the example providers\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n include_providers={\"exmpl\", \"optimade\"},\n)\nclient.get()\n
Exclude certain providers:
Command linePython# Exclude example providers from global list\noptimade-get --exclude-providers exmpl,optimade\n
# Exclude example providers from global list\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n exclude_providers={\"exmpl\", \"optimade\"},\n)\nclient.get()\n
Exclude particular databases by URL:
Command linePython# Exclude specific example databases\noptimade-get --exclude-databases https://example.org/optimade,https://optimade.org/example\n
# Exclude specific example databases\nfrom optimade.client import OptimadeClient\nclient = OptimadeClient(\n exclude_databases={\"https://example.org/optimade\", \"https://optimade.org/example\"}\n)\nclient.get()\n
"},{"location":"getting_started/client/#filtering","title":"Filtering","text":"By default, an empty filter will be used (which will return all entries in a database). You can specify your desired filter as follows (note the quotation marks):
Command linePythonoptimade-get --filter 'elements HAS \"Ag\" AND nsites < 2' --output-file results.json\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nclient.get('elements HAS \"Ag\" AND nsites < 2')\n
The filter will be validated against the optimade-python-tools
reference grammar before it is sent to the underlying servers.
At the command-line, the results of the query will be printed to stdout
, ready to be redirected to a file or piped into another program. For example:
optimade-get --filter 'nsites = 1' --output-file results.json https://optimade.fly.dev\ncat results.json\n
has the following (truncated) output:
{\n // The endpoint that was queried\n \"structures\": {\n // The filter applied to that endpoint\n \"nsites = 1\": {\n // The base URL of the OPTIMADE API\n \"https://optimade.fly.dev\": {\n // The OPTIMADE API response as if called with an infinite `page_limit`\n \"data\": [\n {\n \"id\": \"mpf_1\",\n \"type\": \"structures\",\n \"attributes\": {\n ...\n }\n \"relationships\": {\n ...\n }\n },\n ...\n ],\n \"errors\": [],\n \"links\": {\n \"next\": null,\n \"prev\": null\n },\n \"included\": [\n ...\n ],\n \"meta\": {\n ...\n }\n }\n }\n }\n}\n
The response is broken down by queried endpoint, filter and then base URL so that the query URL can be easily reconstructed. This is the same format as the cached results of the Python client:
from optimade.client import OptimadeClient\nclient = OptimadeClient(base_urls=\"https://optimade.fly.dev\")\nclient.get('nsites = 1')\nclient.get('nsites = 2')\nprint(client.all_results)\n
will return a dictionary with top-level keys:
{\n \"structures\": {\n \"nsites = 1\": {\n \"https://optimade.fly.dev\": {...}\n },\n \"nsites = 2\": {\n \"https://optimade.fly.dev\": {...}\n }\n }\n}\n
For a given session, this cache can be written and reloaded into an OPTIMADE client object to avoid needing to repeat queries.
Info
In a future release, this cache will be automatically restored from disk and will obey defined cache lifetimes.
"},{"location":"getting_started/client/#querying-other-endpoints","title":"Querying other endpoints","text":"The client can also query other endpoints, rather than just the default /structures
endpoint. This includes any provider-specific extensions/<example>
endpoints that may be implemented at a given base URL, which can be found listed at the corresponding /info
endpoint for that database.
In the CLI, this is done with the --endpoint
flag. In the Python interface, the different endpoints can be queried as attributes of the client class or equivalently as a paramter to client.get()
or client.count()
(see below).
optimade-get --endpoint \"structures\"\noptimade-get --endpoint \"references\"\noptimade-get --endpoint \"info\"\noptimade-get --endpoint \"info/structures\"\noptimade-get --endpoint \"extensions/properties\"\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\n\nclient.references.count()\nclient.count(endpoint=\"references\")\n\nclient.info.get()\nclient.get(endpoint=\"info\")\n\nclient.info.structures.get()\nclient.get(endpoint=\"info/structures\")\n\nclient.extensions.properties.get()\nclient.get(endpoint=\"extensions/properties\")\n
"},{"location":"getting_started/client/#listing-the-properties-served-by-a-database","title":"Listing the properties served by a database","text":"You can also retrieve the list of properties served by a database using the --list-properties
flag:
optimade-get --list-properties structures --include-providers odbx\n
from optimade.client import OptimadeClient\nclient = OptimadeClient(include_providers={\"odbx\"})\nclient.list_properties(\"structures\")\n
and do simple string matching filtering of the response:
Command linePythonoptimade-get --list-properties structures --search-property gap\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nclient.list_properties(\"structures\")\n
"},{"location":"getting_started/client/#limiting-the-number-of-responses","title":"Limiting the number of responses","text":"Querying all OPTIMADE APIs without limiting the number of entries can result in a lot of data. The client will limit the number of results returned per database to the value of max_results_per_provider
(defaults: 1000 for Python, 10 for CLI). This limit will be enforced up to a difference of the default page limit for the underlying OPTIMADE API (which is used everywhere). This parameter can be controlled via the --max-results-per-provider 10
at the CLI, or as an argument to OptimadeClient(max_results_per_provider=10)
.
Setting this to a value of -1
or 0
(or additionally None
, if using the Python interface) will remove the limit on the number of results per provider. In the CLI, this setting should be used alongside --output-file
or redirection to avoid overflowing your terminal!
Downloading all the results for a given query can require hundreds or thousands of requests, depending on the number of results and the database's page limit. It is possible to just count the number of results before downloading the entries themselves, which only requires 1 request per database. This is achieved via the --count
flag in the CLI, or the .count()
method in the Python interface. We can use this to repeat the queries from the OPTIMADE paper:
optimade-get \\\n --count \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\"' \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\" AND nelements=2' \\\n --filter 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\" AND NOT elements HAS \"Pb\" AND elements LENGTH 3'\n
from optimade.client import OptimadeClient\nclient = OptimadeClient()\nfilters = [\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\"',\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\", \"Pb\" AND nelements=2'\n 'elements HAS ANY \"C\", \"Si\", \"Ge\", \"Sn\" AND NOT elements HAS \"Pb\" AND elements LENGTH 3'\n]\nfor f in filters:\n client.count(f)\n
which, at the timing of writing, yields the results:
{\n \"structures\": {\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\", \\\"Pb\\\"\": {\n \"http://aflow.org/API/optimade/\": null,\n \"https://www.crystallography.net/cod/optimade\": 436062,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 487,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 1427,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 3719,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 9592,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 1093,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 4395,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 348,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 503,\n \"https://optimade.materialsproject.org\": 30351,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 4451056,\n \"https://optimade.odbx.science\": 55,\n \"http://optimade.openmaterialsdb.se\": 58718,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 2632,\n \"http://optimade.2dmatpedia.org\": 1172\n },\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\", \\\"Pb\\\" AND nelements=2\": {\n \"http://aflow.org/API/optimade/\": 63011,\n \"https://www.crystallography.net/cod/optimade\": 3968,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 2,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 779,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 334,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 1566,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 276,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 24,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 0,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 0,\n \"https://optimade.materialsproject.org\": 3728,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 587923,\n \"https://optimade.odbx.science\": 54,\n \"http://optimade.openmaterialsdb.se\": 690,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 296,\n \"http://optimade.2dmatpedia.org\": 739\n },\n \"elements HAS ANY \\\"C\\\", \\\"Si\\\", \\\"Ge\\\", \\\"Sn\\\" AND NOT elements HAS \\\"Pb\\\" AND elements LENGTH 3\": {\n \"http://aflow.org/API/optimade/\": null,\n \"https://www.crystallography.net/cod/optimade\": 33776,\n \"https://aiida.materialscloud.org/sssplibrary/optimade\": 0,\n \"https://aiida.materialscloud.org/2dstructures/optimade\": 378,\n \"https://aiida.materialscloud.org/2dtopo/optimade\": 0,\n \"https://aiida.materialscloud.org/tc-applicability/optimade\": 144,\n \"https://aiida.materialscloud.org/3dd/optimade\": null,\n \"https://aiida.materialscloud.org/mc3d-structures/optimade\": 4398,\n \"https://aiida.materialscloud.org/autowannier/optimade\": 74,\n \"https://aiida.materialscloud.org/curated-cofs/optimade\": 1447,\n \"https://aiida.materialscloud.org/stoceriaitf/optimade\": 0,\n \"https://aiida.materialscloud.org/pyrene-mofs/optimade\": 0,\n \"https://aiida.materialscloud.org/tin-antimony-sulfoiodide/optimade\": 0,\n \"https://optimade.materialsproject.org\": 11559,\n \"https://api.mpds.io\": null,\n \"https://nomad-lab.eu/prod/rae/optimade/\": 2092989,\n \"https://optimade.odbx.science\": 0,\n \"http://optimade.openmaterialsdb.se\": 7428,\n \"http://oqmd.org/optimade/\": null,\n \"https://jarvis.nist.gov/optimade/jarvisdft\": null,\n \"https://www.crystallography.net/tcod/optimade\": 661,\n \"http://optimade.2dmatpedia.org\": 255\n }\n }\n}\n
"},{"location":"getting_started/client/#callbacks","title":"Callbacks","text":"In Python, the client can also be initialized with a list of callbacks. These will be executed after every request and will have access to the JSON response and the request URL.
For example, callbacks be used, to save to a file or write to a database, without having to pull all the results into memory. Care should be taken if combining an asynchronous client with callbacks, as the callbacks may depend on the execution order of various asynchronous requests and the callbacks themselves may be blocking.
Python# Write a callback that saves into a MongoDB (fake or otherwise)\nimport mongomock as pymongo\nfrom optimade.client import OptimadeClient\n\ndb = pymongo.MongoClient().database.structures\n\ndef write_to_db(url, results):\n for entry in results[\"data\"]:\n entry.update(entry.pop(\"attributes\"))\n entry[\"immutable_id\"] = url\n db.insert_one(entry)\n\nclient = OptimadeClient(callbacks=[write_to_db], silent=True)\n\nclient.get()\n\nprint(db.find_one())\n
Callbacks can also optionally return a dictionary of data that can be used by the client.
Currently the supported keys are:
next
(string): To override the next URL returned by the API. This can be used to dynamically change queries based on the results, or to skip pages that have already been queried previously.advance_results
(integer): To skip the progress bar forward by the set amount.When multiple callbacks are provided, only the final callback will have its result captured.
"},{"location":"getting_started/setting_up_an_api/","title":"Setting up an OPTIMADE API","text":"These notes describe how to set up and customize an OPTIMADE API based on the reference server in this package for some existing crystal structure data.
To follow this guide, you will need to have a working development installation, as described in the installation instructions. Complete examples of APIs that use this package are described in the Use Cases section.
"},{"location":"getting_started/setting_up_an_api/#setting-up-the-database","title":"Setting up the database","text":"The optimade
reference server requires a data source per OPTIMADE entry type (structures
, references
, links
). In the simplest case, these can be configured as named MongoDB collections with a defined MongoDB URI and database name (see below), but they can also be set up as custom subclasses of EntryCollection
that could simply read from a static file. In the reference server, these data sources, or collections, are created in the submodule for the corresponding routers/endpoints.
Here, we shall use the built-in MongoDB collections for each entry type, by simply specifying the appropriate options in the configuration, namely \"database_backend\": \"mongodb\"
, \"mongo_uri\": \"mongodb://localhost:27017\"
, \"mongo_database\": \"optimade\"
and the collection names for each entry type (\"structures_collection\": \"structures\"
etc.). These notes will now assume that you have a MongoDB instance running and you have created a database that matches your \"mongo_database\"
config option.
If you disable inserting test data (with the \"insert_test_data\": false
configuration option), you can test your API/database connection by running the web server with uvicorn optimade.server.main:app --port 5000
and visiting the (hopefully empty) structures endpoint at localhost:5000/v1/structures
(or your chosen base URL).
Note
As of version v0.16, the other supported database backend is Elasticsearch. If you are interested in using another backend, or would like it to be supported in the optimade
package, please raise an issue on GitHub and visit the notes on implementing new filter transformers.
There are two ways to work with data that does not exactly match the OPTIMADE specification, both of which require configuring a subclass of BaseResourceMapper
that converts your stored data format into an OPTIMADE-compliant entry. The two options are:
The main consideration when choosing these options is not necessarily how closely your data matches the OPTIMADE format, but instead how readily the OPTIMADE filtering of that document can be mapped into the corresponding database query. This could require writing or extending the BaseFilterTransformer
class, which takes an OPTIMADE filter string and converts it into a backend-specific query.
For example, if your database stores chemical formulae with extraneous \"1\"'s, e.g., SiO2 is represented as \"Si1O2\"
, then the incoming OPTIMADE filter (which asserts that elements must be alphabetical, and \"1\"'s must be omitted) for chemical_formula_reduced=\"O2Si\"
will also need to be transformed so that the corresponding database query matches the stored string, which in this case can be done easily. Instead, if you are storing chemical formulae as an unreduced count per simulation cell, e.g., \"Si4O8\"
, then it is impossible to remap the filter chemical_formula_reduced=\"O2Si\"
such that it matches all structures with the correct formula unit (e.g., \"SiO2\"
, \"Si2O4\"
, ...). This would then instead require option 2 above, namely either the addition of auxiliary fields that store the correct (or mappable) OPTIMADE format in the database, or the creation of a secondary database that returns the pre-converted structures.
In the simplest case, the mapper classes can be used to define aliases between fields in the database and the OPTIMADE field name; these can be configured via the aliases
option as a dictionary mapping stored in a dictionary under the appropriate endpoint name, e.g. \"aliases\": {\"structures\": {\"chemical_formula_reduced\": \"my_chem_form\"}}
, or defined as part of a custom mapper class.
In either option, you should now be able to insert your data into the corresponding MongoDB (or otherwise) collection.
"},{"location":"getting_started/setting_up_an_api/#serving-custom-fieldsproperties","title":"Serving custom fields/properties","text":"According to the OPTIMADE specification, any field not standardized in the specification must be prefixed with an appropriate \"provider prefix\" (e.g., \"_aflow
\" for AFLOW and \"_cod
\" for COD). This prefix is intended to be unique across all OPTIMADE providers to enable filters to work across different implementations. The prefix can be set in the configuration as part of the provider
option.
Once the prefix has been set, custom fields can be listed by endpoint in the provider_fields
configuration option. Filters that use the prefixed form of these fields will then be passed through to the underlying database without the prefix, and then the prefix will be reinstated in the response.
Example
Example JSON config file fragment for adding two fields to each of the structures
and references
endpoints, that will be served as, e.g., _exmpl_cell_volume
if the provider.prefix
is set to 'exmpl'.
\"provider_fields\": {\n \"structures\": [\"cell_volume\", \"total_energy\"],\n \"references\": [\"orcid\", \"num_citations\"],\n}\n
It is recommended that you provide a description, type and unit for each custom field that can be returned at the corresponding /info/<entry_type>
endpoint. This can be achieved by providing a dictionary per field at provider_fields
, rather than a simple list.
Example
Example JSON config file fragment for adding a description, type and unit for the custom _exmpl_cell_volume
field, which will cause it to be added to the /info/structures
endpoint.
\"provider_fields\": {\n \"structures\": [\n {\n \"name\": \"cell_volume\",\n \"description\": \"The volume of the cell per formula unit.\",\n \"unit\": \"Ao3\",\n \"type\": \"float\"\n },\n \"total_energy\"\n ],\n \"references\": [\"orcid\", \"num_citations\"],\n}\n
"},{"location":"getting_started/setting_up_an_api/#extending-the-pydantic-models","title":"Extending the pydantic models","text":"The pydantic models can also be extended with your custom fields. This can be useful for validation, and for generating custom OpenAPI schemas for your implementation. To do this, the underlying EntryResourceAttributes
model will need to be sub-classed, the pydantic fields added to that class, and the server adjusted to make use of those models in responses. In this case, it may be easier to write a custom endpoint for your entry type, that copies the existing reference endpoint.
Your custom model will need to be registered in three places:
ENTRY_INFO_SCHEMAS
dictionary.Finally, the model must be instructed to use the prefixed (aliased) fields when generating its schemas.
Pulling all of this together:
from optimade.server.schemas import ENTRY_INFO_SCHEMAS\nfrom optimade.models import (\n StructureResource, StructureResourceAttributes, OptimadeField\n)\n\n\nclass MyStructureResourceAttributes(StructureResourceAttributes):\n my_custom_field: str = OptimadeField(\n \"default value\",\n description=\"This is a custom field\",\n )\n\n class Config:\n \"\"\"Add a pydantic `Config` that defines the alias generator,\n based on our configured `provider_fields`.\n\n \"\"\"\n @classmethod\n def alias_generator(cls, name: str) -> str:\n if name in CONFIG.provider_fields.get(\"structures\", []):\n return f\"_{CONFIG.provider.prefix}_{name}\"\n return name\n\n\nclass MyStructureResource(StructureResource):\n attributes: MyStructureResourceAttributes\n\n\nENTRY_INFO_SCHEMAS[\"structures\"] = MyStructureResource.schema\n
Currently, the reference server is not flexible enough to use custom response classes via configuration only (there is an open issue tracking this #929), so instead the code will need to be forked and modified for your implementation.
Note
A similar procedure can be followed for the URL query parameter classes EntryListingQueryParams
and SingleEntryQueryParams
so that custom query parameters can be defined for your API. The individual API routes then need to be adjusted to use the custom query parameter classes. By default, the reference server will validate the incoming query parameters against these classes. If you want to use custom query parameters without redefining the classes mentioned above, you can disable this behaviour by setting the configuration option validate_query_parameters
to false, after which all query parameters will be passed on to the corresponding router method (e.g., database queries).
With the database collections, mappers, aliases and provider configured, you can try running the web server (with e.g., uvicorn optimade.server.main:app
, if your app is in the same file as the reference server) and validating it as an OPTIMADE API, following the validation guide.
If you host your API at a persistent URL, you should consider registering as an OPTIMADE provider, which will add you to the federated list used by users and clients to discover data. Instructions for how to do this can be found at in the Materials-Consortia/providers repository.
"},{"location":"getting_started/use_cases/","title":"Example use cases","text":""},{"location":"getting_started/use_cases/#serving-a-single-database","title":"Serving a single database","text":"The Materials Project uses optimade-python-tools
alongside their existing API and MongoDB database, providing OPTIMADE-compliant access to highly-curated density-functional theory calculations across all known inorganic materials.
optimade-python-tools
handles filter parsing, database query generation and response validation by running the reference server implementation with minimal configuration.
odbx, a small database of results from crystal structure prediction calculations, follows a similar approach. This implementation is open source, available on GitHub at ml-evs/odbx.science.
"},{"location":"getting_started/use_cases/#serving-multiple-databases","title":"Serving multiple databases","text":"Materials Cloud uses optimade-python-tools
as a library to provide an OPTIMADE API entry to archived computational materials studies, created with the AiiDA Python framework and published through their archive. In this case, each individual study and archive entry has its own database and separate API entry. The Python classes within the optimade
package have been extended to make use of AiiDA and its underlying PostgreSQL storage engine.
Details of this implementation can be found on GitHub at aiidateam/aiida-optimade.
"},{"location":"getting_started/use_cases/#extending-an-existing-api","title":"Extending an existing API","text":"NOMAD uses optimade-python-tools
as a library to add OPTIMADE API endpoints to an existing web app. Their implementation uses the Elasticsearch database backend to filter on millions of structures from aggregated first-principles calculations provided by their users and partners. NOMAD also uses the package to implement a GUI search bar that accepts the OPTIMADE filter language. NOMAD uses the release versions of the optimade-python-tools
package, performing all customisation via configuration and sub-classing. The NOMAD OPTIMADE API implementation is available in the NOMAD FAIR GitLab repository.
This use case is demonstrated in the example Integrate OPTIMADE with an existing web application.
"}]} \ No newline at end of file diff --git a/latest/sitemap.xml b/latest/sitemap.xml index c01ca8ff5..ca241f6dc 100644 --- a/latest/sitemap.xml +++ b/latest/sitemap.xml @@ -2,302 +2,302 @@p__UF3id}?cg%nuE-l$v2H^Yt3dB8xR+IK2O4UtU zSE-`XMz5pO %2b!bhsBT4r{UG?Y}$AoSO)RKCR`3zBJN9;Z8n>6P{K92Ft)zRLa~Y)A?q5m z$@uedb~cEwwuAlJ?lsl6n6 Dwz9HjB-ZMXX%Y^SO|$m9$6Zm7 z+Lp76$;F4!`D8Tt7(ddr?f0D>cK-hu-+o@tZ@%B%-@t(G=ZjyrUmq5?a~RX@{P*Je z?hb`RTIX>O99uv{g*8i6HJ&8ielUm0soe9DeGCMg@k_z3 _S*&_u8C!$}32dK7H#U31)$aQ&W3Vj wMu!UNtgMA{u zRv{g)^Hx}yl7t?~JSPO-)+801bi}|ztwJj{(k{?_?E;wye3F++$4k lYh>; zbzrS{GGS?b-H){Mqn2dg+!|C7n0&EHMqOJ|SxjwLhG;JcWtP~;7!|zY0Bwu|0Dj49 z7Aha1p-;h@_y)95EAxzfy8CbB?cs!$9wo8Z%;2mE;L4-1C^A#F4qOO4$cy8-bsQP( zE920_Ls2qcktuaM;z)vv%|x!CcEp;Lfd}ThArUrHmGQROkY{M-pN9RmO*N^;zIFQP zcVr{>+(REKW5%=13#&C3`-qu90{p)HO-ovS;57aaZ=KtfNsu$;6Dx3%cOpekp-#By zNlM4{w%OJ9_mvaeBGemhB2XQ3g7`<5h7~>*^wLCPLC;Mr7T}i!dTIB>DkyeMT9qUg zyNvOw1U~Pt$k#QThd0}bO6aDZfP(!&)*UmThf7O#+HULJWVkg#%HR}xj%>;ZS3WkE zD7cD6;w6>`A>XpF9wj){g>JiXIhh=2!s`^;1kMZkr-_f